JavaScript`魔法のキーワード`this
5947 ワード
前言
グローバルコードのthis
ここではすべてが簡単です.グローバルコードでは、thisは常にグローバルオブジェクト自体を指します.
1誤解
関数コードで
2 thisとは何ですか?
thisは、動作中にバインディングされており、作成時(定義時)にバインディングされていません.このバインディング規則は、関数呼び出し時の様々な条件に依存します.
2.1標準バインディング
まず、最も一般的な関数コールタイプを紹介します.独立関数コール、すなわち直接
PS:厳密なモードであれば、デフォルトのバインディングは、グローバルオブジェクトではなくundefinedとなります.
2.2暗黙的バインディング
関数がオブジェクトとして使用される方法(実はすべての属性)である場合は、暗黙的なバインディング規則になります.
2.3明示的なバインディング
関数が
さらに、ES 5には、関数を実行しないように結合関数の
2.4 newバインディング
これは最後のバインディング規則で、構造関数を呼び出しに新しいオブジェクトを作成(構成)します. このオブジェクトに対してプロトタイプリンク この新しいオブジェクトは、関数呼び出しの を指す.関数が他のオブジェクトに戻っていない場合、この新しいオブジェクト に戻ります.
全体として
2.5優先度
関数呼び出し中の
newバインディング>明示的バインディング>陰的バインディング>標準バインディング
2.6 ES 6の矢印関数
ES 6における新しい姿勢としては、矢印関数のthisが上記規則以外に凌駕され、外層作用領域によってthisが決定される.
おわりに
全体として、呼び出し位置が見つけられたら、その規則を解析すれば、正確に
知識は「あなたの知らないJavaScript」から吸収します.
this
はJavaScriptコードの中でほとんど随所に見られます.同時に、JavaScriptの中の比較的複雑なメカニズムの一つです.フロントエンドの開発者ははっきりした認識がない場合、this
は完全に魔法であり、つかみにくいです.グローバルコードのthis
ここではすべてが簡単です.グローバルコードでは、thisは常にグローバルオブジェクト自体を指します.
//
this.a = 1;
console.log(a); // 1
//
//
b = 2;
console.log(this.b); // 2
//
//
c = 3;
console.log(this.c); //3
関数コードのthis1誤解
関数コードで
this
を使うのは面白いです.this
を関数自体に向けて容易に理解します.実はそうではないです.次のコードを見てみましょう.function foo(num) {
console.log("foo: " + num);
// foo
this.count++;
}
foo.count = 0;
for (var i = 1; i < 3; i++) {
foo(i);
}
// foo: 1
// foo: 2
// foo ?
console.log(foo.count); // 0 -- WTF?
console.log
文は確かに2回出力されたが、foo.count
は依然として0であり、字面の意味からthis
が間違っていることを理解するのは明らかである.foo.count = 0
を実行すると、確かに関数オブジェクトfoo
に属性count
が追加されます.しかし、関数内部のthis.count
のthis
は、その関数オブジェクトを指すものではない.では、thisはいったい誰を指していますか?2 thisとは何ですか?
thisは、動作中にバインディングされており、作成時(定義時)にバインディングされていません.このバインディング規則は、関数呼び出し時の様々な条件に依存します.
2.1標準バインディング
まず、最も一般的な関数コールタイプを紹介します.独立関数コール、すなわち直接
( )
の呼び出し方法です.この規則は他の規則を適用できない場合のデフォルトと見なすことができます.function foo() {
console.log(this.a);
}
var a = 2;
foo(); // 2
グローバルスコープ内で宣言された変数var a = 2
(グローバルオブジェクトの属性でもある)が、関数foo()
の呼び出し時に印刷されたことに留意すべきである.これにより、デフォルトのバインディングのthisはグローバルオブジェクトを指すことがわかった.PS:厳密なモードであれば、デフォルトのバインディングは、グローバルオブジェクトではなくundefinedとなります.
2.2暗黙的バインディング
関数がオブジェクトとして使用される方法(実はすべての属性)である場合は、暗黙的なバインディング規則になります.
function foo() {
console.log(this.a);
}
var a = 1;
var obj = {
a: 2,
foo: foo
};
obj.foo(); // 2
ここでの関数foo
は、オブジェクトobj
の属性に参照されたものであり、次いで . ()
で呼び出される.このとき、this.a
はobj.a
の値を得ることができ、this
がobj
を指していることが分かります.(どのオブジェクトによって呼び出したらどのオブジェクトを指すのか)もう一つの暗黙的な損失に注意しています.function foo() {
console.log( this.a );
}
var obj = {
a: 2,
foo: foo
};
var bar = obj.foo; // !
var a = "oops, global"; // a
bar(); // "oops, global"
bar
はobj.foo
の1つの参照であるが、実際には、2つはfoo
関数自体を参照するだけで、bar()
はデフォルトのバインディングの呼び出しだけである!暗黙的な損失は通常、コールバック関数で見られます.以下に述べた表示バインディングで解決できます.2.3明示的なバインディング
関数が
call
およびapply
法によって呼び出されるとき、私たちはディスプレイバインディングと呼ぶ.function foo() {
console.log(this.a);
}
var obj = {
a: 2
};
foo.call(obj); // 2
foo.apply(obj); // 2
foo.call(obj)
とfoo.apply(obj)
を通して、foo
のthis
を指しました.obj
のバインディング角度から言えば、this
とcall
は同じです.その違いは他のパラメータに反映されています.さらに、ES 5には、関数を実行しないように結合関数の
apply
だけを指すbind
方法が提供されている.function foo(something) {
console.log(this.a, something);
}
var obj = {
a: 2
};
var bar = foo.bind(obj);
bar(3); // 2 3
this
は単にfoo.bind(obj)
を結びつけた関数を返しただけで、this
を呼び出した時にはすでにデフォルトのバインディングではなく、この方法を利用して上記の暗黙的な損失問題を解決することができます.//
function foo() {
console.log(this.a);
}
var obj = {
a: 'obj',
foo: foo
};
var a = 'global';
setTimeout(obj.foo, 100); // "global"
// bind
function foo() {
console.log(this.a);
}
var obj = {
a: 'obj',
foo: foo
};
var a = 'global';
setTimeout(obj.foo.bind(obj), 100); // "obj"
私たちは、元のコードのbar(3)
をコールパラメータとしてobj.foo
に伝えたが、setTimeout
の指向がなくなったのはなぜですか?this
は呼び出しの方式によって指し示しが確定されると言っていますが、ここでは関数参照だけを呼び出していません.内部呼び出しの時はデフォルトのバインディング規則が適用されているかもしれません.その後、this
はグローバルオブジェクトを指しています.this
の疑似コードは以下の通りです.function setTimeout (fn, delay) {
// delay
fn(); // ,
}
setTimeout
をバインディングして返した関数をコールバックパラメータとして使用して、bind
が指向を失う問題を解決しました.2.4 newバインディング
これは最後のバインディング規則で、構造関数を呼び出しに
this
を使用すると、newバインディング規則です.function foo(a) {
this.a = a;
}
var bar = new foo(2);
console.log( bar.a ); // 2
new
を使用して関数を呼び出すと、以下のような動作が行われる.new
を行う(これは重要ではない)bar.__proto__ = foo.prototype
、つまりthis
のthis.a = a
に結び付けられ、新しいオブジェクト全体として
this
を使用してnew
を呼び出すと、関数のfoo()
は、関数が作成した新しいオブジェクトを指しています.2.5優先度
関数呼び出し中の
this
によって結合された4つのルールが分かりました.あなたが必要なのは、関数呼び出しの位置を見つけて、どのルールが適用されているかを判断することです.this
の方向が分かります.しかし、いくつかのルールを適用したら、最終的にどのルールが勝者となりますか?自分で試してみてもいいです.ここでは多く説明しないで、直接に答えを出してください.newバインディング>明示的バインディング>陰的バインディング>標準バインディング
2.6 ES 6の矢印関数
ES 6における新しい姿勢としては、矢印関数のthisが上記規則以外に凌駕され、外層作用領域によってthisが決定される.
function foo() {
setTimeout(() => {
// this foo()
console.log(this.a);
}, 100);
}
var obj = {
a: 2
};
foo.call(obj); // 2
明示的なバインディングthis
によって内部矢印関数のfoo
も引き継がれた.実はこれはシンタックス飴にすぎません.プライベート変数を使ってthis
を保存して、コードを変換しました.function foo() {
var self = this; // foo `this`
setTimeout(function() {
console.log(self.a);
}, 100);
}
var obj = {
a: 2
};
foo.call(obj); // 2
でも、書き方ではかっこいいです.ははは!おわりに
全体として、呼び出し位置が見つけられたら、その規則を解析すれば、正確に
this
を見つけることができる.初めてブログを書きます.ちょっと乱れているかもしれません.レンガを持って死んでください.知識は「あなたの知らないJavaScript」から吸収します.