「this」キーワードは関数内でどのように機能しますか?
-
02-07-2019 - |
質問
JavaScript で興味深い状況に遭遇しました。オブジェクトリテラル表記を使用して複数のオブジェクトを定義するメソッドを持つクラスがあります。それらのオブジェクトの内部では、 this
ポインタが使用されています。プログラムの動作から、次のことが推測されました。 this
ポインタは、リテラルによって作成されるオブジェクトではなく、メソッドが呼び出されたクラスを参照します。
これは恣意的なように思えますが、私が期待する動作方法です。これは定義された動作ですか?クロスブラウザーは安全ですか?「仕様にそう記載されている」を超えて、なぜそのようになっているのかという根本的な理由はありますか(たとえば、より広範な設計上の決定/哲学の結果であるかなど)?簡素化されたコード例:
// inside class definition, itself an object literal, we have this function:
onRender: function() {
this.menuItems = this.menuItems.concat([
{
text: 'Group by Module',
rptletdiv: this
},
{
text: 'Group by Status',
rptletdiv: this
}]);
// etc
}
解決
私の別の投稿から引用したものですが、ここにはあなたが知りたかった以上のものがあります これ.
始める前に、JavaScript について心に留めておき、意味がわからない場合は自分に言い聞かせる最も重要な点を説明します。JavaScript にはクラスがありません (ES6 class
は 糖衣構文)。何かがクラスのように見える場合、それは巧妙なトリックです。JavaScript には オブジェクト そして 機能. 。(これは 100% 正確ではありません。関数は単なるオブジェクトですが、それらを別のものとして考えると役立つ場合があります)
の これ 変数は関数に付けられます。関数を呼び出すたびに、 これ 関数の呼び出し方法に応じて、特定の値が与えられます。これは、多くの場合、呼び出しパターンと呼ばれます。
JavaScript で関数を呼び出すには 4 つの方法があります。関数を次のように呼び出すことができます。 方法, 、 として 関数, 、 として コンストラクタ, 、そして 適用する.
方法として
メソッドはオブジェクトに付加される関数です
var foo = {};
foo.someMethod = function(){
alert(this);
}
メソッドとして呼び出すと、 これ 関数/メソッドが含まれるオブジェクトにバインドされます。この例では、これは foo にバインドされます。
機能として
スタンドアロン機能がある場合は、 これ 変数は「グローバル」オブジェクトにバインドされ、ほとんどの場合、 窓 ブラウザのコンテキスト内のオブジェクト。
var foo = function(){
alert(this);
}
foo();
これがあなたをつまずかせている原因かもしれません, 、でも悪く思わないでください。多くの人は、これは間違った設計上の決定だと考えています。コールバックはメソッドとしてではなく関数として呼び出されるため、一貫性のない動作が見られるのはこのためです。
多くの人は次のようなことをして問題を回避します。
var foo = {};
foo.someMethod = function (){
var that=this;
function bar(){
alert(that);
}
}
変数を定義します それ を指す これ. 。Closure (それ自体がトピック) は維持されます それ したがって、bar をコールバックとして呼び出した場合でも、参照は残ります。
注記:で use strict
関数として使用する場合はモード、 this
グローバルにバインドされていません。(それは undefined
).
コンストラクターとして
関数をコンストラクターとして呼び出すこともできます。使用している命名規則 (TestObject) に基づいて、これも それがあなたがやっていること、そしてそれがあなたをつまずかせているのかもしれません.
new キーワードを使用して関数をコンストラクターとして呼び出します。
function Foo(){
this.confusing = 'hell yeah';
}
var myObject = new Foo();
コンストラクターとして呼び出すと、新しいオブジェクトが作成され、 これ そのオブジェクトにバインドされます。繰り返しますが、内部関数があり、それがコールバックとして使用される場合は、それらを関数として呼び出すことになります。 これ グローバルオブジェクトにバインドされます。その var that = このトリック/パターンを使用してください。
一部の人々は、constructor/new キーワードは、クラスに似たものを作成する方法として Java/従来の OOP プログラマに投げかけられた骨子だと考えています。
applyメソッドを使用する場合
最後に、すべての関数には「apply」という名前のメソッドがあります (はい、関数は Javascript のオブジェクトです)。[適用] を使用すると、次の値を決定できます。 これ となり、引数の配列を渡すこともできます。ここで役に立たない例を示します。
function foo(a,b){
alert(a);
alert(b);
alert(this);
}
var args = ['ah','be'];
foo.apply('omg',args);
他のヒント
関数呼び出し
関数はオブジェクトの一種にすぎません。
すべての Function オブジェクトには、 電話 そして 適用する 呼び出された Function オブジェクトを実行するメソッド。
呼び出されるとき、これらのメソッドの最初の引数は、メソッドによって参照されるオブジェクトを指定します。 this
関数の実行中のキーワード - の場合 null
または undefined
, 、グローバル オブジェクト、 window
, 、のために使用されます this
.
したがって、関数を呼び出すと...
whereAmI = "window";
function foo()
{
return "this is " + this.whereAmI + " with " + arguments.length + " + arguments";
}
...括弧付き - foo()
- と同等です foo.call(undefined)
または foo.apply(undefined)
, 、つまり 効果的に と同じ foo.call(window)
または foo.apply(window)
.
>>> foo()
"this is window with 0 arguments"
>>> foo.call()
"this is window with 0 arguments"
追加の引数 call
は関数呼び出しの引数として渡されますが、関数呼び出しには 1 つの追加引数が渡されます。 apply
関数呼び出しの引数を配列のようなオブジェクトとして指定できます。
したがって、 foo(1, 2, 3)
と同等です foo.call(null, 1, 2, 3)
または foo.apply(null, [1, 2, 3])
.
>>> foo(1, 2, 3)
"this is window with 3 arguments"
>>> foo.apply(null, [1, 2, 3])
"this is window with 3 arguments"
関数がオブジェクトのプロパティである場合...
var obj =
{
whereAmI: "obj",
foo: foo
};
...オブジェクト経由で関数への参照にアクセスし、かっこを使用して関数を呼び出す - obj.foo()
- と同等です foo.call(obj)
または foo.apply(obj)
.
ただし、オブジェクトのプロパティとして保持される関数は、それらのオブジェクトに「バインド」されません。の定義からもわかるように、 obj
上で説明したように、関数は単なるオブジェクトの一種であるため、参照することができます (したがって、関数呼び出しに参照によって渡したり、関数呼び出しから参照によって返すことができます)。関数への参照が渡されるとき、それが渡された場所に関する追加情報はありません から が一緒に運ばれるため、次のようなことが起こります。
>>> baz = obj.foo;
>>> baz();
"this is window with 0 arguments"
関数リファレンスへの呼び出し、 baz
, 、呼び出しのコンテキストを提供しないため、実質的には次と同じです。 baz.call(undefined)
, 、 それで this
結局参照することになる window
. 。もし望むなら baz
それが属していることを知ること obj
, 、次の場合に何らかの方法でその情報を提供する必要があります。 baz
が呼び出されます。ここで最初の引数が指定されます。 call
または apply
そしてクロージャーが登場します。
スコープチェーン
function bind(func, context)
{
return function()
{
func.apply(context, arguments);
};
}
Function が実行されると、新しいスコープが作成され、それを囲んでいるスコープへの参照が作成されます。上記の例で匿名関数が作成されると、その関数は作成されたスコープへの参照を持ちます。 bind
の範囲。これは「クロージャ」として知られています。
[global scope (window)] - whereAmI, foo, obj, baz
|
[bind scope] - func, context
|
[anonymous scope]
変数にアクセスしようとすると、この「スコープ チェーン」をたどって、指定された名前の変数を見つけます。現在のスコープに変数が含まれていない場合は、チェーン内の次のスコープを調べ、次のスコープに到達するまで続きます。グローバルスコープ。無名関数が返されたときと、 bind
実行が終了しても、匿名関数にはまだへの参照が残っています。 bind
のスコープなので、 bind
のスコープは「消える」ことはありません。
上記のすべてを考慮すると、次の例でスコープがどのように機能するか、および特定の値を持つ「事前バインド」で関数を渡すテクニックがなぜ使用されるのかを理解できるはずです。 this
呼び出されたときに機能します:
>>> baz = bind(obj.foo, obj);
>>> baz(1, 2);
"this is obj with 2 arguments"
これは定義された動作ですか?クロスブラウザーは安全ですか?
はい。はい。
なぜそれがそうであるかの根底にある理由はありますか...
の意味 this
推測するのは非常に簡単です:
- もし
this
コンストラクター関数内で使用されており、関数はnew
キーワード、this
作成されるオブジェクトを指します。this
パブリックメソッドでもオブジェクトを意味し続けます。 - もし
this
ネストされたものを含む他の場所で使用される 保護された 関数の場合、グローバル スコープ (ブラウザの場合はウィンドウ オブジェクト) を指します。
2 番目のケースは明らかに設計上の欠陥ですが、クロージャを使用することで非常に簡単に回避できます。
この場合、内側の this
ではなくグローバル オブジェクトにバインドされます。 this
外部関数の変数。それが言語の設計方法です。
「JavaScript:」を参照してください。良い部分については、ダグラス・クロックフォード著「The Good Parts」を参照してください。
についての素晴らしいチュートリアルを見つけました ECMAScript はこれ
Aこの値は、実行コンテキストに関連する特別なオブジェクトです。したがって、コンテキスト オブジェクトとして名前を付けることができます (つまり、コンテキストの実行コンテキストがアクティブ化されるオブジェクト)。
任意のオブジェクトをコンテキストのこの値として使用できます。
Aこの値は実行コンテキストのプロパティですが、変数オブジェクトのプロパティではありません。
変数とは対照的に、この値は識別子の解決プロセスに関与しないため、この機能は非常に重要です。つまり、コードでこれにアクセスすると、その値はスコープ チェーンの検索なしで、実行コンテキストから直接取得されます。この値は、コンテキストに入るときに 1 回だけ決定されます。
グローバル コンテキストでは、 this 値はグローバル オブジェクト自体です (つまり、ここでのこの値は変数オブジェクトに等しい)
関数コンテキストの場合、単一の関数呼び出しごとにこの値が異なる場合があります。
参照 JavaScript の核心 そして 第3章-これ