ホームページ > ウェブフロントエンド > jsチュートリアル > JavaScript ECMA-262-3 の詳細な分析第 3 章 this_javascript スキル。

JavaScript ECMA-262-3 の詳細な分析第 3 章 this_javascript スキル。

WBOY
リリース: 2016-05-16 18:01:37
オリジナル
814 人が閲覧しました

はじめに
この記事では、実行コンテキストに直接関連する詳細について説明します。話題になっているのはこのキーワードです。
このトピックは難しいことが実践で証明されており、さまざまな実行コンテキストで this の値を決定する際に問題が頻繁に発生します。
多くのプログラマーは、プログラミング言語では this キーワードがオブジェクト指向プログラム開発と密接に関連しており、コンストラクターによって新しく作成されたオブジェクトを完全に指していると信じ込んでいます。これは ECMAScript 仕様にも実装されていますが、後で説明するように、ECMAScript では、これは新しく作成されたオブジェクトを指すことだけに限定されません。
ECMAScript におけるこれの値を詳しく見てみましょう。
定義
これは実行コンテキストの属性です:

コードをコピー コードは次のとおりです:

activeExecutionContext = {
VO: {...},
this: thisValue
};

ここで、VO は、前に説明した変数オブジェクトです。前の章。
これは、コンテキスト内の実行可能コード (の種類) に直接関係します。この値はコンテキストに入るときに決定され、コンテキストがコードを実行している間は変更されません。
これらのシナリオをさらに詳しく調べてみましょう。
グローバル コードでの this の値
ここではすべてが単純です。グローバル コードでは、これは常にグローバル オブジェクト自体であるため、間接的に参照することができます。
コードをコピー コードは次のとおりです:

//
の明示的なプロパティ定義// グローバル オブジェクト
this.a = 10; // global.a = 10
alert(a); // 10
// 非修飾識別子
への割り当てによる暗黙的な定義🎜>b = 20;
alert(this.b); // 20
// グローバル コンテキスト
の変数オブジェクトはグローバル オブジェクトであるため、暗黙的にも使用されます。
var c = 30;
alert(this.c); // 30


関数コードで this を使用するのは興味深いことです、この種のアプリケーション シナリオは難しく、多くの問題を引き起こします。
このタイプのコードにおける this 値の最初の (おそらく最も重要な) 特徴は、この値が関数に静的にバインドされていないことです。
上で述べたように、this の値はコンテキストに入るときに決定されますが、関数コードでは、this の値は毎回 (コンテキストに入るとき) 完全に異なる場合があります。
何があっても、コードの実行中、 this の値は変更されません。つまり、これは変数ではないため、新しい値を割り当てることはできません。 (対照的に、Python プログラミング言語では、オブジェクト自体として明示的に定義され、実行時に継続的に変更できます)。



コードをコピー コードは次のとおりです。 var foo = {x: 10};
var bar = {
x: 20,
test: function () {
alert(this === bar) // true
alert(this.x); 20
this = foo; // エラー
alert(this.x) // エラーがなかった場合は 10 ではなく 20
}
};コンテキストに入ると、この値は
// 「bar」オブジェクトとして決定されます。その理由は、
// 以下で詳しく説明します。
bar.test() // true, 20
foo .test = bar.test;
// ただし、ここでは同じ関数
foo.test() を呼び出しているにもかかわらず、この値は
// を参照します。 , 10


では、関数コードの中で、 this の値が変化するのは何でしょうか?いくつかの要因があります。
まず、通常の関数呼び出しでは、これはコンテキスト コード、つまり呼び出し関数の親コンテキストをアクティブにする呼び出し元によって提供されます。これは関数の呼び出し方法によって異なります。 (翻訳者注: ここを参照)
どのような状況でも this の値を正確に決定するには、この重要な点を理解して覚えておく必要があります。つまり、this の値に影響を与えるのは関数の呼び出し方法です。呼び出しのコンテキストでは他には何もありません (一部の記事や JavaScript に関する書籍でも、「this の値は関数の定義方法に依存し、グローバル関数の場合は次のように設定されます)」と主張されています。グローバル オブジェクト、関数がオブジェクトのメソッドである場合、これは常にオブジェクトを指しますが、これは絶対に当てはまりません)。トピックを続けると、通常のグローバル関数でも呼び出し方法が異なるためにアクティブ化され、これらの呼び出し方法が異なると this の異なる値が生成されることがわかります。




コードをコピー
コードは次のとおりです:

function foo() {
alert(this);
}
foo(); // グローバル
alert(foo === foo.prototype.constructor); / true
// ただし、同じ関数の別の形式の呼び出し式
// では、この値は異なります
foo.prototype.constructor() // foo.prototype
場合によっては、関数がオブジェクトのメソッドとして呼び出され、this の値がこのオブジェクトに設定されないことがあります。

コードをコピー コードは次のとおりです:
var foo = {
bar: function () {
alert(this);
alert(this === foo)
}
foo.bar(); var exampleFunc = foo.bar;
alert(exampleFunc === foo.bar); // true
// 同じ関数の別の形式の呼び出し式
// を使用します。 this value
exampleFunc(); // グローバル、false


では、関数の呼び出し方法は this の値にどのように影響するのでしょうか?この値がどのように決定されるかを完全に理解するには、内部型 (参照型) を詳細に分析する必要があります。
参照型
疑似コードを使用すると、参照型は 2 つのプロパティを持つオブジェクトとして表すことができます。base (つまり、プロパティを持つオブジェクト) と、base 内の propertyName です。



コードをコピー コードは次のとおりです: var valueOfReferenceType = {
base: ,
propertyName:


参照型の値は次の 2 つの状況でのみ存在します。識別子 When;(識別子を扱うとき;)
2. またはプロパティ アクセサーを使用します。(またはプロパティ アクセサーを使用します)
識別子の処理については、第 4 章で説明します。このメソッドを使用した場合の戻り値は常に参照型の値であることに注意してください (これはこの点で重要です)。
識別子は、グローバル オブジェクト内の変数名、関数名、関数パラメータ名、および認識されないプロパティ名です。たとえば、次の識別子の値:
var foo = 10;
function bar() {}
演算の中間結果における、対応する参照型の値は次のとおりです。 🎜>



コードをコピーします

コードは次のとおりです: var fooReference = { base: global , propertyName: 'foo' };
var barReference = {
base: global,
propertyName: 'bar'
};参照型からオブジェクトの実オブジェクトを取得するには、疑似コードの GetValue メソッド (翻訳者注: 11.1.6) で次のように Value を表すことができます:




コードをコピー

コードは次のとおりです:

function GetValue(value) { if (Type(value) != Reference) { 戻り値; } varbase = GetBase(value); if (base === null) { throw new ReferenceError;
returnbase.[[ Get]](GetPropertyName(value));
}


内部 [[Get]] メソッドは、プロトタイプ チェーン内の継承されたプロパティの分析を含む、オブジェクトのプロパティの実際の値を返します。
GetValue(fooReference); // 10
GetValue(barReference); // 関数オブジェクト "bar"
属性アクセサーはすべておなじみです。これには、ドット (.) 構文 (プロパティ名が正しい識別子で、事前にわかっている場合) と括弧構文 ([]) の 2 つのバリエーションがあります。
foo.bar();
foo['bar']();
途中の戻り値の計算では、対応する参照型の値は次のとおりです。 🎜>

コードをコピー


コードは次のとおりです:


var fooBarReference = {
base: foo,
プロパティ名: 'バー'
} ;
GetValue(fooBarReference); // 関数オブジェクト "bar"
では、最も重要な意味で、参照型の値は関数コンテキストでの this の値にどのように関連しているのでしょうか?この関連付けのプロセスがこの記事の中心です。 (与えられた瞬間がこの記事の中心です。) 関数コンテキストで this の値を決定するための一般的な規則は次のとおりです:
関数コンテキストでは、this の値は呼び出し元によって提供され、次によって決定されます。関数の呼び出し方法。呼び出し括弧 () の左側が参照型値の場合、これは参照型値の基本オブジェクトに設定されます。それ以外の場合 (参照型とは異なるその他のプロパティ)、this の値は次のようになります。ヌル。ただし、this の値が null である実際の状況はありません。this の値が null の場合、その値は暗黙的にグローバル オブジェクトに変換されるからです。
例を見てみましょう:
コードをコピーします コードは次のとおりです:

function foo () {
return this;
}
foo(); // global

呼び出し側の括弧の左側が参照型であることがわかります。値 (foo は識別子であるため):
コードをコピー コードは次のとおりです:

var fooReference = {
base : global,
propertyName: 'foo'
};

同様に、これも参照型の基本オブジェクトに設定されます。それがグローバルオブジェクトです。
同様に、プロパティ アクセサーを使用します。
コードをコピーします。 コードは次のとおりです。

var foo = {
bar: function () {
return this;
}
} // foo

同様に、ベースが foo オブジェクトである参照型の値があり、関数バーがアクティブになると、ベースがこれに設定されます。


var fooBarReference = {
base: foo,
propertyName: 'bar'
};


ただし、同じ関数が別の方法でアクティブ化された場合、この値は異なります。
var test = foo.bar;
test(); // global
test は識別子として使用されるため、他の参照型の値が生成され、この値のベース (グローバル オブジェクト) が設定されますこの値に。


var testReference = {
base: global,
propertyName: 'test'
};


これで、同じ関数を異なる形式でアクティブ化すると異なる結果が得られる理由を明確に説明できます。その答えは、異なる参照にあります。型の中間値 (型 Reference)。


function foo() {
alert (this) ;
}
foo(); // グローバル、なぜなら
var fooReference = {
base: global,
propertyName: 'foo'
}; alert(foo === foo.prototype.constructor); // true
// 呼び出し式の別の形式
foo.prototype.constructor(); // foo.prototype、
var fooPrototypeConstructorReference = {
base: foo.prototype,
propertyName: 'constructor'
};


次の呼び出しによって this の値を動的に決定する別の古典的な例:


コードをコピー コードは次のとおりです。 function foo() {
alert(this.bar) );
}
var x = {bar: 10};
x.test = foo; >x.test( ; ただし、他の型の場合、 this の値は自動的に null に設定され、 this の値は実際には暗黙的にグローバル オブジェクトに変換されます。
次の関数式について考えてみましょう:




コードをコピー

コードは次のとおりです:


(function () {
alert(this); // null => グローバル
})();
この例では、関数オブジェクトはありますが、参照型オブジェクトはありません (識別子またはプロパティ アクセサーではないため)。したがって、this の値は最終的にグローバル オブジェクトに設定されます。
より複雑な例:
コードをコピーします コードは次のとおりです:

var foo = {
bar: function () {
alert(this);
foo.bar(); // 参照、OK => (foo .bar)(); // 参照、OK => foo
(foo.bar = foo.bar)(); // グローバル? / global?
(foo.bar, foo.bar)(); // グローバル?


では、なぜ中間値が参照型値である必要があるのでしょうか? 、しかし、一部の呼び出しでは、 this の値がベース オブジェクトではなく、グローバル オブジェクトであることがわかります。
この問題は、次の 3 回の呼び出しで発生します。特定の操作を実行した後、呼び出し括弧の左側の値は参照型ではなくなります。
最初の例は明らかです - 明らかな参照型です。結果として、これが基本オブジェクト、つまり foo になります。
2 番目の例では、グループ化演算子 (翻訳者注: ここでのグループ化演算子は foo.bar の外側の大括弧 "()" を指します) には実際的な意味はありません。参照型を取得するメソッドから上記のことを考えてください。 GetValue などのオブジェクトの実際の値 (11.1.6 を参照)。同様に、グループ化操作の戻り値でも、取得されるのは参照型のままです。これが、this の値がベース オブジェクト (foo) に再度設定される理由です。
3 番目の例では、グループ化演算子とは異なり、代入演算子は GetValue メソッドを呼び出します (11.13.1 の 3 番目のステップを参照)。返された結果はすでに関数オブジェクト (参照型ではない) です。つまり、 this の値は null に設定され、実際の最終結果はグローバル オブジェクトに設定されます。
4 番目と 5 番目は同じです。これに応じて、コンマ演算子と論理演算子 (OR) は GetValue メソッドを呼び出します。そのため、この値は再び に設定されます。グローバルオブジェクト。
参照型であり、これは null
呼び出し式が呼び出し括弧の左側で参照型の値を決定する状況があります。翻訳者注、元のテキストは少し遅れています!)。 、 this の値が null に設定されている限り、最終的には暗黙的にグローバルに変換されます。この状況は、参照型値の基本オブジェクトがアクティブ化オブジェクトである場合に発生します。
次の例では、内部関数が親関数によって呼び出されます。このとき、上記の特殊な状況がわかります。第 2 章で学んだように、ローカル変数、内部関数、および仮パラメータは、特定の関数のアクティベーション オブジェクトに格納されます。




コードをコピー
コードは次のとおりです。 function foo() { function bar() {
alert(this); // グローバル
}
bar() // AO.bar() と同じ
}


アクティブ化object は常に this の値として null を返します (つまり、疑似コード AO.bar() は null.bar() と同等です)。 (訳者注: 理解できない場合は、こちらを参照してください) ここで再び上記の状況に戻り、最終的に this の値がグローバル オブジェクトに設定されます。
例外があります: 「関数が with ステートメントで呼び出され、関数名の属性が with オブジェクトに含まれている場合 (訳者注: 以下の例では __withObject)」。 with ステートメントは、そのオブジェクトをスコープ チェーンの先頭、つまりアクティブ化オブジェクトの前に追加します。同様に、参照型は (識別子またはプロパティ アクセサーを通じて) 値を持ち、その基本オブジェクトはアクティブ化オブジェクトではなく、with ステートメントのオブジェクトになります。ちなみに、この状況は内部関数だけでなく、グローバル関数にも関係します。これは、 with オブジェクトがスコープ チェーンの最前面のオブジェクト (グローバル オブジェクトまたはアクティベーション オブジェクト) よりも前にあるためです。



コードをコピーします
コードは次のとおりです。 var x = 10; ({ foo: function () {
alert(this.x);
},
x: 20
}) {
foo(); >}
// because
var fooReference = {
base: __withObject,
propertyName: 'foo'
};


実際のパラメータの関数catch ステートメント 同様の状況が呼び出しにも存在します。この場合、catch オブジェクトはスコープの前、つまりアクティベーション オブジェクトまたはグローバル オブジェクトの前に追加されます。ただし、この特定の動作は ECMA-262-3 のバグであることが確認され、新しいバージョンの ECMA-262-5 で修正されました。修正後、特定のアクティベーション オブジェクト内で、this はグローバル オブジェクトを指します。 catch オブジェクトではなく。



コードをコピー

コードは次のとおりです:

try {
throw function () {
alert(this);
} catch (e) {
e(); ES3、グローバル - ES5 で修正
}
// アイデアに基づいて
var eReference = {
base: __catchObject,
propertyName: 'e'
};ただし、これはバグであるため、
// この値はグローバルに強制されます
// null => global
var eReference = {
base: global,
propertyName: 'e '
};


名前付き関数の再帰呼び出しでも同じ状況が発生します (関数の詳細については、第 5 章「関数」を参照してください)。関数の最初の呼び出しでは、ベース オブジェクトは親アクティブ化オブジェクト (またはグローバル オブジェクト) です。再帰呼び出しでは、ベース オブジェクトは関数式のオプションの名前を格納する特定のオブジェクトである必要があります。ただし、この場合、this の値は常に global に設定されます。


(function foo(bar) {
alert( this);
!bar && foo(1) // 特別なオブジェクトである必要がありますが、常に (正しい) global
})(); 🎜>this コンストラクターとして呼び出される関数の値
this の値が関数のコンテキストで関連するもう 1 つの状況は、関数がコンストラクターとして呼び出される場合です。



コードをコピー
コードは次のとおりです。 function A() { alert (this) ; // 新しく作成されたオブジェクト、以下 - "a" オブジェクト this.x = 10>}
var a = new A(); // 10


この例では、新しい演算子は「A」関数内で [[Construct]] メソッドを呼び出し、オブジェクトの作成後にその内部の [[Call]] メソッドを呼び出します。 、すべて同じ関数「A」は両方とも this の値を新しく作成されたオブジェクトに設定します。
関数呼び出しの this を手動で設定する
関数の呼び出し時に this の値を手動で設定できるようにするために、Function.prototype で 2 つのメソッドが定義されています。それらは .apply メソッドと .call メソッドです (すべての関数で使用できます)。アクセスしてください)。彼らは this の値として受け入れる最初の引数を使用します。これは呼び出しスコープで使用されます。 2 つのメソッドに大きな違いはありません。.apply の場合、2 番目のパラメータは配列 (または引数などの配列のようなオブジェクト) である必要があります。これに対して、.call は任意のパラメータを受け入れることができます。両方のメソッドの必須パラメータです。最初の ——this.
alert(this.b) {
alert(c); // this === グローバル, this .b == 10, c == 20
a.call({b: 20}, 30) // this === {b: 20}, this.b == 20, c == 30
a.apply({b: 30}, [40]) // this === {b: 30}, this.b == 30, c == 40


結論
この記事では、ECMAScript におけるこのキーワードの特性について説明します (これらは実際には、対照的に、C や Java の機能です。翻訳者注: この文はあまり役に立ちません。まだわかりません) OK、今は読みません)。この記事が ECMAScript でこのキーワードがどのように機能するかを正確に理解するのに役立つことを願っています。同様に、コメントでの質問に喜んでお答えします。
その他参照
10.1.7 – これ; 11.1.1 – このキーワード; 11.2.3 – 関数呼び出し: ECMA- 262-3 の詳細。第 3 章。中国語のアドレス: [JavaScript]ECMA-262-3。第 3 章。この 翻訳ステートメント:1。この記事は、いくつかの章で氏の翻訳を参照しています。参考部分は記事全体の約 30% を占め、残りの 70% は完全に再翻訳されています 2. 翻訳プロセス中に、私は次のことを相談しました。翻訳を読むときは、原文のリストを参照してください。
3. 翻訳がどんなに優れていても、原文と一致することはできません。翻訳を読んだ後、原文を注意深く読んでください。
関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート