りあくとp89。「式と演算子で短く書く」と「JavaScriptの鬼門、thisを理解する」という項目を復習します。
又の名を「短絡評価」と呼ぶ。&&(アンパサンド)や||(パイプライン)、!といった論理演算子が左から右に評価される性質を利用して、右辺の評価を左辺の評価に委ねる記法のこと。
OR演算子||は左辺がfalsyな値だと評価が右辺に渡される。変数の初期化を動的に行いたい場合等に使用する。
サンプルコード
const test = undifined || null || 0 || NaN || '' || 'test' ;
この場合はfalsyな値が続いているので、最終的にもっとも右の文字列'test'が代入される。
AND演算子&&は左辺がtruthyな値だと評価が右辺に渡される。falsyな値があればそれ以降の右辺は無視される
サンプルコード
const test2 = ' ' && 100 && [] && {} && 'Chao!';
この記法は左辺がfalsyだと右辺が評価・実行されないため、if文の代わりに使用できる。
サンプルコード
true && console.log('hello') # hello
false && console.log('hello') # (no output)
ES2020で導入された記法。「何かを実行して返ってきた値が失敗を示すものだったら、代わりに別のものを入れたい」ケースには、OR演算子||によるショートサーキット評価よりもこちらのNullish Coalescing とOptional Chainingを用いる方法が良いとされる
通常のオブジェクトに対するプロパティアクセス演算子は . と [] の 2つ。これらによって指定したキーのプロパティが存在してなかった場合、それが 1 階層めであれば undefined が返るだけだが、その参照が2階層以上に渡って外れているとタイプエラーになる。
ところが代わりに ?. 演算子を使うと、チェーン内の各参照が正しいかどうかを明示的に確認せずにアクセスしていくことができる。途中のプロパティが存在していなかったら、そこで式が短絡されて undefined を返してくれる。
サンプルコード
「coalesce」というのは「癒着する、合体する」という意味の単語である。OR演算子 || に似ているが、ちがうのは左辺が null または undefined のときだけ右辺が評価されるということである。よって 0 や ' ' の空文字といった falsy な値はそのまま評価される。
サンプルコード
OR演算子だと、0や空文字もスルーされてしまう為、より明示的なnullish coalescingの方が望ましい。
・OR演算子 || は、左がfalsyな値の場合に右を返す。変数に代入する値を同的に変化させたい場合に有効。
・AND演算子 && は、左がtruthyな値の場合に右を返す。左がfalsyな値であれば右が実行されない為、if文の代わりに使用できる。
・Optional Chaining ( .? 演算子 ) は、タイプエラーを出すことなくオブジェクトの存在しないプロパティにアクセスしていくことができる。
・Nullish Coalescing ( ?? 演算子 ) は、左がnull、又はundefinedの場合のみに右を返す。よって0などのfalsyな値はそのまま評価される。
・OR演算子と似ているが、より明示的なNullish Coalescingを使用する方が良いとされる。
りあくとと合わせていろんな教材見ながらthisを勉強していきます。
https://www.youtube.com/watch?v=sIGrsliVj4U
thisとは、関数を呼び出すオブジェクトへのリンクである。
例えばこんなオブジェクトが定義されていたとします。
//オブジェクトを定義
const test = {
name: 'taro',
//オブジェクトのキーに関数がある
getName: function(){
//this.nameを出力
return this.name
}
}
thisとは、「関数を呼び出すオブジェクトへのリンク」なので、
オブジェクトtestの、getNameのバリューにある関数の返り値にあるthis.nameのthisは、オブジェクトtestを表します。
なので、test.getName();とした場合、返り値として帰ってくるのは、「taro」になります。
次は、グローバルな関数を定義して、
1, グローバルに呼ぶ場合
2, 関数をオブジェクトのプロパティにつけた場合
この二つを比較していきたいと思います。
まずはthisを出力するグローバルな関数を定義します。
//ログにthisを出力する関数を定義
const sayThis = function () {console.log(this);};
この関数をオブジェクトのプロパティにつけて呼び出すのも試したいので、オブジェクトを作ります。
//オブジェクトを定義
const obj = {name: 'taro'};
オブジェクトのプロパティに先ほど作った関数をつけてみます。
//objのsayThisというプロパティに関数sayThisを定義
obj.sayThis = sayThis;
まずは関数をグローバルに呼んだ場合のthisの挙動を見ていきます。
sayThis(); // ▶︎ window
windowオブジェクトが出力されました。
それではこの関数をオブジェクトのプロパティにつけて呼んでみたいと思います。
obj.sayThis(); // ▶︎ {name: "taro", sayThis: ƒ}
objオブジェクトが出力されました。
thisが、関数を呼び出すオブジェクトを参照していることがわかります。
コンストラクタとは、日本語に訳すと「構築子」となり、JavaScriptではインスタンス(実態)を作成する関数のことをコンストラクタ又はコンストラクタ関数と呼びます。
ES6より以前はクラス構文がなかった為、コンストラクタを使用することで擬似的にクラスを再現していました。
コンストラクタからインスタンス(実態)を作成するためには、new演算子を使用します。
普通の関数の違いは、new演算子によって実態を作る関数であるというところです。
それではコンストラクタでのthisの挙動を見ていきましょう。
まずはコンストラクタ関数を定義します。
//コンストラクタ関数を定義
const Person = function (name) {
//引数に受け取ったnameをnameプロパティに設定
this.name = name;
};
new演算子を使用してコンストラクタからインスタンス(実態)を作っていきます。
//インスタンス生成
const taro = new Person("taro");
ログを出力してみると、コンストラクタ関数Personから、このようなインスタンス(実態)が生成されていました。
console.log(taro); // ▶︎ Person {name: "taro"}
コンストラクタ関数でのthisは、new演算子によって作成されたオブジェクトがthisに参照されているようです。
コンストラクタ関数は普通の関数としても使用することができるので、new演算子を使用せずに、引数に値を渡す形でも呼び出してみます。
const test = Person("test");
test.nameで生成された値を呼び出してみましたがエラーになりました。
console.log(test.name); // ▶︎TypeError
これは、関数Personをコンストラクタ関数としてではなく、グローバル関数として呼び出したためです。
なので先程引数に渡したnameはwindowオブジェクトに格納されています。
console.log(window.name); ▶︎ "test"
・new演算子によって呼び出した時は、コンストラクタ関数内のthisはインスタンスオブジェクトを参照
・グローバルオブジェクトとして呼び出した時は、コンストラクタ関数内のthisはwindowオブジェクトを参照
コンストラクタでも「thisは関数を呼び出すオブジェクトを参照」していることがわかりました。
入れ子関数とは、関数の中で関数を定義して呼んでいるもの。
実際にみた方がわかりやすいと思うので、入れ子関数を定義してみます。
//オブジェクトを定義
const test = {
//メソッドを定義 (入れ子関数になっている)
sayHello: function () {
//thisを出力
console.log(this);
//関数を定義
const func = function () {
//thisを出力
console.log(this);
};
//関数を呼び出し
func();
},
};
メソッドとは、オブジェクトのバリューに入っている関数のことです。
メソッド内のthisと、メソッドの中で定義した関数内のthis。どのように違うのでしょうか。
実際に見ていきましょう
test.sayHello();
const test = {
sayHello: function () {
console.log(this); //▶︎ {sayHello: ƒ} //ƒはfunctionの略
const func = function () {
console.log(this); //▶︎ window
};
func();
},
};
1.sayHelloメソッド内のthisにはtestオブジェクト
2.sayHelloメソッド内に定義したfunc関数内のthisにはwindowオブジェクト
がそれぞれ格納されてあることがわかりました。
1のような挙動になったのは、sayHelloメソッドをオブジェクトのプロパティとして呼び出したからです。
thisは「関数を呼び出すオブジェクトへのリンク」なので、sayHelloメソッド内のthisには、sayHelloメソッドを呼び出しているオブジェクト、つまりtestオブジェクトが格納されています。
2のような挙動になったのは、func関数をsayHelloメソッド内で、オブジェクトのプロパティとしてではなく、グローバルオブジェクトとして呼び出したからです。thisは「関数を呼び出すオブジェクトへのリンク」なので、sayHelloメソッド内で、グローバルオブジェクトとして呼び出されたfunc関数内のthisには、func関数を呼び出すオブジェクト、つまりwindowオブジェクトが格納されています。
入れ子関数でも、「thisは関数を呼び出すオブジェクトを参照」していることがわかりました。
コールバック関数とは、ある関数を呼び出す時に、引数に指定する別の関数のこと。
//オブジェクトを定義
const test = {
//メソッドを定義 (入れ子関数になっている)
sayHello: function () {
//thisを出力
console.log(this); // {sayHello: ƒ}
//1秒後に第一引数に渡した関数を実行するsetTimeout関数を定義
//コールバック関数
setTimeout(function () {
//thisを出力
console.log(this); //▶︎ window
}, 1000);
},
};
コールバック関数では、functionの呼ばれ方によりthisが変化します。
なぜなら、functionを呼んでいるオブジェクトがthisになるからです。
上の例でいうとsetTimeout関数はグローバルで呼ばれるので、コールバック関数内のthisはwindowになります。
ここでいうfunctionとは、function(){}
で定義する関数のことで、arrow関数とは、() => {}
で定義する関数のこと。
functionとarrow関数ではthisの挙動に違いがあるので注意しましょう。
//オブジェクトを定義
const test = {
//メソッドを定義 (入れ子関数になっている)
sayHello: function () {
//thisを出力
console.log(this); // {sayHello: ƒ}
//1秒後に第一引数に渡した関数を実行するsetTimeout関数を定義
setTimeout(function () {
//thisを出力
console.log(this); //▶︎ window
}, 1000);
},
};
先程のコードと全く同じなので説明は省きます。
//オブジェクトを定義
const test = {
//メソッドを定義 (入れ子関数になっている)
sayHello: function () {
//thisを出力
console.log(this); // {sayHello: ƒ}
//1秒後に第一引数に渡した関数を実行するsetTimeout関数を定義
setTimeout(() => {
//thisを出力
console.log(this); //▶︎ {sayHello: ƒ}
}, 1000);
},
};
お気づきでしょうか、sayHelloメソッド内で出力されるthisがfunction関数ではwindowだったのに対してarrow関数ではtestオブジェクトになっています。
arrow関数で関数を定義すると、定義時点でどこから呼ばれたかでthisが決まるようになります。超わかりやすいですね。
そうです!arrow関数を使えば脳死でthisを使うことができます!今までの説明はなんだったんだ!おい!
「thisとは、関数を呼び出すオブジェクトへのリンクである。」
thisの呼び方:thisの中身 という感じでまとめていきます!
・グローバルに呼ぶ場合:windowオブジェクト
・関数をオブジェクトのプロパティにつけた場合:呼び出し元のオブジェクト
・new演算子によって呼び出す場合:インスタンスオブジェクト
・コールバック関数によって呼び出す場合:関数を呼んでいるオブジェクト
・arrow関数で呼び出すのthis:関数が定義されてるオブジェクト
こんな感じでしょうか!
thisが大体どんなものなのかはわかった気がします。
最後までごらんいただきありがとうございました!
@ 酒井悠宇