はじめに
もうすぐ地獄のクリスマスですね。僕は、AirPodsProとiPadと湾曲ディスプレイとRTX2080Tiと銀魂全巻を買ってもらえれば全然余裕です、はい。
ところで、僕はJavaScriptを始めて一年ちょっとになりますがほとんどReactを書いてきました。しかし、いわゆるPure JSとかVanilla JSとかはほとんど書いたことがなく、javascriptについてフィーリングでなんとかしてきた部分も多くありました。基本的な関数とクラスについて勉強したことをまとめます。
関数
参考書
関数の部分ではこちらの本を自分がわかりやすいようにまとめました。
関数の定義方法
関数を定義する方法は大きく二つある
- function命令を使う
- 関数リテラル表現を使う
// function命令を使う
function plus(num1, num2) {
return num1 + num2;
};
// 関数リテラル表現を使う
const plus = function(num1, num2) {
return num1 + num2;
};
-
リテラルはデータの型のことあるが、javascriptの関数はデータ型の一種という分類(関数も値の一つ)になる。そのため、関数を変数に入れたり、関数を関数の引数や戻り値にしたりできる。
-
関数リテラル表現で定義した場合、plusという変数に関数を入れているが、宣言した時には名前を持っていないことから無名関数と呼ばれる。アロー関数は無名関数になる。
-
関数リテラルは、その関数が変数に代入された時に定義され、function命令はコードがコンパイルされた時に定義される。👇(scriptタグ内は別)
// javascriptは上から順番にコードが実行される。 また、コードをコンパイル -> 実行の流れがある。
// function命令
// コンパイル時にfunctionが動き関数が定義される。
// 実行時にはどこからでもhello関数を呼びさせる。
console.log(hello()); // hello world
function hello() {
return 'hello world';
};
// 関数リテラル
// 変数に値が代入されるのは実行のタイミング。
// コンパイル時にfunctionは動かない。
console.log(hello()); // エラー
hello = function() {
return 'hello world';
};
コールバック関数
別の関数の中で呼び出される関数。
function hello() {
console.log('hello world');
};
function nankaiIunen(callback) {
for (let i=0; i<5; i++) {
callback();
};
};
// nankaiIunen関数の引数にhello関数を渡している->関数もリテラルだからできる
// nankaiIunen関数ではhello関数を呼び出している
// hello worldが5回出力される
nankaiIunen(hello);
値(リテラル)として関数を渡すときはカッコを外す。hello() -> hello で渡し、実行したいときにカッコをつける。
関数も値であるっぽいことを確認してみる。
function hello() {
console.log('hello world');
};
console.log(hello);
// 結果
// 定義された関数が文字列として出力される
function hello() {
console.log('hello world');
};
無名関数を放り込んでみる
function nankaiIunen(callback) {
for (let i=0; i<5; i++) {
callback();
};
};
// hello worldが5回出力される
nankaiIunen(() => {
console.log('hello word');
});
// or
nankaiIunen(function(){
console.log('hello world');
});
クロージャ
関数を返す関数で、返された関数がローカル変数を参照していると値が保持される。この振る舞いを持った関数がクロージャ。(通常は関数の中で使われたローカル変数は処理が終わると破棄される。)
// counter関数を実行すると、counter関数のローカル変数であるincreaseの値を
// 1プラスする関数を返す
function counter() {
let increase = 0;
return function() {
return ++increase
};
};
// counter関数から返された無名関数をrun変数に代入(ここで無名関数に名前がつく)
const run = counter();
// run関数を実行
consoel.log(run()); // 1
consoel.log(run()); // 2
consoel.log(run()); // 3
consoel.log(run()); // 4
作った関数をそのまま使うのではなく関数によって返された関数を使うため、クロージャを作るにも使うにも一手間いる。
- 自分自身の変数に変更を加える関数を返す関数を作る
- 任意の変数に返される関数を入れる
- 返された関数を実行する
また、2の工程で複数の変数に返される関数を入れて、それぞれの関数を実行すると、それぞれの環境を持つことができる。
function counter(num) {
let increase = num;
return function() {
return ++increase
};
};
const run1 = counter(10);
const run2 = counter(100);
console.log(run1()); // 11
console.log(run1()); // 12
console.log(run2()); // 101
console.log(run2()); // 102
run1とrun2で同じローカル変数を参照しているように見えるが、counter関数が実行されると独立したスコープができる。
クラス
コンストラクタ
constructorという特別なメソッドで初期設定ができる。このメソッドは初期設定をするだけのため、returnで何か値を返したりはしない。そして、クラスでのthisは自分自身のオブジェクトを示す。(自動でthisオブジェクトを作成して、自動でthisオブジェクトを返してくれるということをやっていて、newキーワードでこれを利用することができる)
class Streamer {
constructor(name, age) {
this.name = name;
this.age = age;
}
};
const stylishnoob = new Streamer('関さん', 29);
console.log(stylishnoob.name); // 関さん
console.log(stylishnoob.age); // 29
// コンストラクターがやっていること
class Streamer {
const this = {};
function init(name, age) {
this.name = name;
this.age = age;
return this;
};
};
メソッド
メソッドの定義の仕方は普通の関数の定義とちょっと違う。
class Streamer {
constructor(name, age) {
this.name = name;
this.age = age;
}
shout() {
console.log('Overwatch大好き!!');
}
// アロー
shout = () => {
console.log('Overwatch大好き!!');
};
};
const stylishnoob = new Streamer('関さん', 29);
stylishnoob.shout(); // Overwatch大好き!!
ゲッターメソッド
クラスのプロパティを取得するメソッド
class Streamer {
constructor(name, age) {
this.name = name;
this.age = age;
}
shout() {
console.log('Overwatch大好き!!');
}
get seki() {
return this.name;
}
};
const stylishnoob = new Streamer('関さん', 29);
console.log(stylishnoob.seki); // 関さん
console.log(stylishnoob.seki()); // TypeError: stylishnoob.seki is not a function
ゲッターメソッドは普通のメソッドと同じように呼び出すと"not function"みたいなエラーが出る。プロパティと同じように呼び出す。
セッターメソッド
クラスのプロパティを設定するメソッド
使い方に少し癖がある
class Streamer {
constructor(name, age) {
this.name = name;
this.age = age;
}
shout() {
console.log('Overwatch大好き!!');
}
get seki() {
return this.name;
}
set lastName(value) {
this.name = value;
}
};
const stylishnoob = new Streamer('関さん', 29);
stylishnoob.lastName = 'ゆうた'; // これでセッターメソッドを使う
console.log(stylishnoob.seki); // ゆうた
静的メソッド
インスタンスを生成しなくても利用できるメソッド
class Streamer {
constructor(name, age) {
this.name = name;
this.age = age;
}
static speak() {
console.log('BAN覚悟でサノバウィッチを配信するわ');
}
};
console.log(Streamer.speak()); // BAN覚悟でサノバウィッチを配信するわ
継承
継承したクラスは継承元のコンストラクタを使うことができる
class Streamer {
constructor(name, age) {
this.name = name;
this.age = age;
}
static speak() {
console.log('BAN覚悟でサノバウィッチを配信するわ');
}
};
class DeToNator extends Streamer {
coment() {
console.log('岩手ナンバーワン!!');
};
};
const spygea = new DeToNator('高橋', 30);
spygea.coment(); // 岩手ナンバーワン!!
spygea.name // 高橋
spygea.age; // 30
オーバーライド
継承元のプロパティを引き継ぎ、新しいプロパティを追加する
class Streamer {
constructor(name, age) {
this.name = name;
this.age = age;
}
static speak() {
console.log('BAN覚悟でサノバウィッチを配信するわ');
}
};
class DeToNator extends Streamer {
constructor(name, age, height) {
super(name, age); // 継承元の使いたいプロパティをsuperに入れて保持しておく
this.height = height; // 新しいプロパティを追加する
}
coment() {
console.log('岩手ナンバーワン!!');
};
};
const spygea = new DeToNator('高橋', 30, '192cm');
console.log(spygea.name); // 高橋
console.log(spygea.age); // 30
console.log(spygea.height); // 192cm
継承元のプロパティを定義し直す
class Streamer {
constructor(name) {
this.name = name;
this.age = 29; // このプロパティを変更したい
}
};
// 確認
const stylishnoob = new Streamer('関さん');
// この時点では当然29
console.log(stylishnoob.age); // 29
class DeToNator extends Streamer {
constructor(name, age, height) {
super(name, age); // プロパティを引き継ぐ
this.age = 30; // 引き継いだ上で上書きする
this.height = height;
}
coment() {
console.log('岩手ナンバーワン!!');
};
};
const spygea = new DeToNator('高橋', '192cm');
console.log(spygea.age); // 30
最後に
コードを実際に書いて動作を確認するのは、とても分かりやすく勉強になりました。ぜひChromeのコンソールなどで試してみてください。
そして非リア充の皆さん、今年のクリスマスも一致団結して乗り越えましょう!
また来年もよろしくお願いします!