JavaScrip Primer
thisはいろんな本を読んでやっと理解できるようになってきた
コンテキストの概念をちゃんと理解することで頭の中でイメージがしやすくなった気がする。
thisの参照先は主に次の条件によって変化する。
- 実行コンテキストにおけるthis
- コンストラクタにおけるthis
- 関数とメソッドにおけるthis
- Arrow Functionにおけるthis
プロトタイプオブジェクトの概念はPythonでもあるから動的型付け言語によく用いられる手法なのかな?
メソッド呼び出しにおけるthisが問題となるパターン1
完全に理解した気がする
問題: thisを含むメソッドを変数に代入した場合
"use strict";
const person = {
fullName: "Brendan Eich",
sayName: function() {
// `this`は呼び出し元によって異なる
return this.fullName;
}
};
// `sayName`メソッドは`person`オブジェクトに所属する
// `this`は`person`オブジェクトとなる
console.log(person.sayName()); // => "Brendan Eich"
// `person.sayName`を`say`変数に代入する
const say = person.sayName;
// 代入したメソッドを関数として呼ぶ
// この`say`関数はどのオブジェクトにも所属していない
// `this`はundefinedとなるため例外を投げる
say(); // => TypeError: Cannot read property 'fullName' of undefined
対応方法
- メソッドを変数に代入するな。
- thisを明示的に指定する
- 関数をラップする
メソッド呼び出しにおけるthisが問題となるパターン2
問題: コールバック関数とthis
"use strict";
// strict modeを明示しているのは、thisがグローバルオブジェクトに暗黙的に変換されるのを防止するため
const Prefixer = {
prefix: "pre",
/**
* `strings`配列の各要素にprefixをつける
*/
prefixArray(strings) {
return strings.map(function(str) {
// コールバック関数における`this`は`undefined`となる(strict mode)
// そのため`this.prefix`は`undefined.prefix`となり例外が発生する
return this.prefix + "-" + str;
});
}
};
// `prefixArray`メソッドにおける`this`は`Prefixer`
Prefixer.prefixArray(["a", "b", "c"]); // => TypeError: Cannot read property 'prefix' of undefined
対応方法
- thisを一時変数へ代入する(下を使うことが多い)
- Arrow Functionでコールバック関数を扱う
Arrow Functionとthis
- Arrow Function で定義されたthisは関数の定義時(静的)に決まる
- Arrow Function以外は関数の実行時(動的)に決まる
静的に決まるとは、Arrow Functionの外側のスコープ(関数)のthisを参照する。
さらに、Arrow Function はthisを暗黙的な引数として受け付けない
変数におけるスコープチェーンの仕組みと同じだな〜
コンテキストの種類とスコープ範囲分かると繋がってくるな
急にメソッド短縮記法を書かないで
いや、出てたけど忘れてただけか
this理解するの大変すぎるけど何度目かの挑戦でもう少しで理解できそうだからちゃんと理解するか
はえー、知らなかった。
Errorオブジェクトはインスタンスの作成時に、そのインスタンスが作成されたファイル名や行数などのデバッグに役立つ情報を持っています。
console.error()で出力するとスタックトレースもコンソールに出力してくれるらしい。
ビルトインエラー
- ReferenceError
- SyntaxError
- TypeErro
元のエラーオブジェクトのスタックトレースが失われる
エラーオブジェクト受け取って再度エラーオブジェクトを返すと、元のエラーオブジェクトのスタックトレースが失われる。
それを解決するにはErrorオブジェクトのcauseオプションを使用する
} catch (e) {
throw new Error("二つ目のエラー", {cause: e})
}
実行順番について
なんかエラーをキャッチできないやつあったけどそういうことだったのかー!と理解した。tryという実行コンテキストがあってその中のエラーだけをキャッチしているイメージなのかな?
非同期処理では、try...catch構文を使っても非同期的に発生した例外をキャッチできません。 次のコードでは、10ミリ秒後に非同期的なエラーを発生させています。 しかし、try...catch構文では次のような非同期エラーをキャッチできません。
try {
setTimeout(() => {
throw new Error("非同期的なエラー");
}, 10);
} catch (error) {
// 非同期エラーはキャッチできないため、この行は実行されません
}
console.log("この行は実行されます");
Promiseオブジェクトは非同期処理の状態や結果をラップするオブジェクトという説明がしっくりきた。
catchに.thenを繋ぐとエラー後に実行される
以下の仕様があるため、catchに.thenを繋ぐとエラー後に実行される。
catchはFulfilled状態のPromiseインスタンスを作成して返すため
thenやcatchメソッドは常に新しいPromiseインスタンスを作成して返すという仕様
[ES2022] Module直下ではawait式が使える
確かに、modulesではawaitそのまま使いたい時あるかも
[コラム] エラーファーストコールバック
元のエラーオブジェクトのスタックトレースが失われる話のやつか
Map/Set
簡単かと思ったらこれも重たかった。
Objectと同じかと思ったら結構違くてびっくり
オブジェクトをキーにとれるからDOMノードや要素に付加情報を付けれるのとかなるほどなと思ったけど、明日になったら大体忘れてそう。
使う場面は多いのかな?実務で出てきたらどう使われているか見るのが一番理解しやすそう
イテレータとジェネレータ
概念はあったけど名前は知らなかった
メモリ効率は配列をループするより、mapの方が効率良いってのは知ってたけどキミのおかげか