このセクションでは、特殊なケースをより理解するための高度なトピックについて説明します。
あなたが速く読み進めたいのであれば、スキップまたは別の機会に見てください。
複雑なメソッド呼び出しは、 this を失う可能性があります。例えば:
let user = {
name: "John",
hi() { alert(this.name); },
bye() { alert("Bye"); }
};
user.hi(); // John (シンプルな呼び出しは動作します)
// 今、name に応じて user.hi または user.bye を読んでみましょう
(user.name == "John" ? user.hi : user.bye)(); // Error!
最後の行では、user.hi か user.bye を選択する三項演算子があります。このケースでは、結果は user.hi です。
メソッドは丸括弧 () ですぐに呼び出されます。しかし、それは正しく動きません!
呼び出しはエラーになります、なぜなら、呼び出しの内側の "this" の値は undefined になるからです。
これは動きます (オブジェクトドットメソッド):
user.hi();
これはダメです (評価されたメソッド):
(user.name == "John" ? user.hi : user.bye)(); // Error!
なぜでしょう?なぜそのようなことが起こるのか理解したい場合、obj.method() の呼び出しがどのように機能するのかを理解してみましょう。
参照型の説明
よく見ると、 obj.method() 文に2つの操作があります:
- まず、ドット
'.'がプロパティobj.methodを抽出します。 - 次に、丸括弧
()でそれを実行します。
そして、this についての情報は最初の処理から2つ目の処理へどのように渡されるでしょう?
それらの操作を別々の行に書いた場合、this が失われるのは明らかでしょう:
let user = {
name: "John",
hi() { alert(this.name); }
}
// メソッドの取得呼び出しを2行に分けます
let hi = user.hi;
hi(); // Error, this は undefined なので
ここで hi = user.hi は関数を変数の中においています。そして最後の行は完全に独立しています。なので、this がありません。
user.hi() 呼び出しを動作させるために、JavaScriptはトリックを使います – ドット '.' は関数ではなく、特別な参照型を返します。
参照型は “仕様上の型” です。私たちは明示的にそれを使うことはできませんが、言語の中で内部的に使われています。
参照型の値は、3つの値の組み合わせ (base, name, strict) です。ここで:
baseはオブジェクトです。nameはプロパティです。strictはuse strictが効いている場合は true です。
user.hi へのプロパティアクセスの結果は、関数ではなく参照型です。strict mode での user.hi はこうなります:
// 参照型の値
(user, "hi", true)
参照型に対して丸括弧 () 呼び出しがされると、それらはオブジェクトとそのメソッドについての完全な情報を受け取り、正しい this (このケースでは user)をセットできます。
参照型はドット . から呼び出し括弧 () へ情報を渡す目的の特別な “中間” の内部型です。
代入 hi = user.hi のような他の操作は、参照型を破棄し、user.hi(関数)の値を渡します。従って、それ以降の操作は全て this を “失います”。
なので、結果として、this の値は、関数がドット obj.method()、もしくは角括弧 obj[method]()構文を使って直接呼び出された場合のみ正しく渡されます。このチュートリアルの後半では、func.bind() など、この問題を解決するためのさまざまな方法を学びます。
サマリ
参照型は言語の内部の型です。
obj.method() 内の . のようなプロパティの読み取りでは、正確なプロパティ値ではなくプロパティ値とそれが取得されたオブジェクトの両方を保持する特別な “参照型” の値を返します。
これはその後に続くメソッド呼び出し () がオブジェクトを取得しそこに this を設定するためです。
その他すべての操作では、参照型は自動的にプロパティ値になります(上のケースでは関数)。
このメカニズム全体は我々の目からは見えません。式を使用して、メソッドがオブジェクトから動的に取得される場合など、微妙なケースでのみ問題になります。
コメント
<code>タグを使ってください。複数行の場合は<pre>を、10行を超える場合にはサンドボックスを使ってください(plnkr, JSBin, codepen…)。