js thisバインドを深く理解する(暗記する必要はなく、末尾に総括と面接問題の解析がある)
14143 ワード
jsのthisバインド問題は、多くの初心者を混乱させ、一部のベテランが気持ち悪いと感じているのは、thisのバインドが「つかみにくい」ためで、間違いを犯すときはなぜか分からないことが多く、かなり逆論理だ.次のコードを考えてみましょう.
普段レンガを運ぶときによくあるthisバインドの問題は、xxxに書いたり出会ったりするかもしれません.onclickがトリガーされたとき、何を出力しますか?
テストを容易にするために、コードを簡略化します.
この小さな例を通して、
1 . thisはJavaScriptのキーワードの一つです.オブジェクトが自動的に生成する内部オブジェクトであり、オブジェクトの内部でのみ使用できます.関数の使用状況によってthisの値が変化します. thisが何を指しているかは、作成時ではなく、どこでどのように呼び出されるかにかかっています.(多くの人が誤解しているところ) 2 .
以下に説明する4つのバインドのルールを把握すると、関数呼び出しを見るだけで
2.1デフォルトバインド
次のコードを考慮します.
これが典型的なデフォルトバインドです.foo呼び出しの位置を見てみましょう.「ライトレバー司令官」というように、修飾されていない関数呼び出しを直接使用すると、デフォルトでデフォルトバインドしか適用できません.
では、デフォルトはどこにバインドされていますか.一般的には
2.隠性バインディング
コード:
答え:undefined 10
関数fooが実行されると、コンテキストオブジェクト、すなわち
チェーン性の関係、例えば
2.3顕性バインディング
2.3.1暗黙的バインドの制限
私たちのさっきの暗黙的なバインドには致命的な制限があります.コンテキストには私たちの関数が含まれなければなりません.例:
2 .3 .2 call apply bind
ここではjsが提供する関数callとapplyを使用します.これらの役割は関数のthis指向を変更し、最初のパラメータはthisオブジェクトを設定することです.
2つの関数の違い: callは、2番目のパラメータからすべてのパラメータが元の関数のパラメータです. applyは2つのパラメータのみを受け入れ、2番目のパラメータは配列でなければなりません.この配列は元の関数のパラメータリストを表します.
例:
call,apply関数のほかにthisを変える関数bindがあり,call,applyとは異なる.
bindには1つの関数しかなく、すぐに実行されません.ただ、1つの値を関数のthisにバインドし、バインドされた関数を返します.例:
(bind関数はとても特別で、今度みんなと一緒にそのソースコードを議論します)
2.3.2顕性バインド
本題を開始し、コードを上げると、上の暗黙的なバインドの例を使用します.
暗黙的なバインド例のコンテキストオブジェクトの関数を削除しました.明らかに
実はcallはfoo上の関数で、thisの指向を変えながらこの関数を実行します.
(深く理解したい[
2.4 newバインド
2.4.1
オブジェクト向けを学んだパートナーはnewに慣れていないに違いない.jsのnewと伝統的なオブジェクト向け言語のnewの役割は新しいオブジェクトを作成することだが、彼らのメカニズムはまったく異なる.
新しいオブジェクトを作成するには、
jsにおけるnewで修飾された関数は「構造関数」であり、正確には関数の
ではnewで関数の新しいオブジェクトを作成します. この新しいオブジェクトの この新しいオブジェクトをこの関数のthisにバインドします. は、この関数が他のオブジェクトを返さない場合、新しいオブジェクトを返します.
3つ目は次のnewバインドです
2.4.2 newバインド
ピッピッピッピッピッピッピッピッピッピッピッピッピッピッピッピッピッピッピッピッピッピッピッピッピッ
newを使用して関数を呼び出すと、関数は独自の名前で名前を付けて新しいオブジェクトを作成し、戻ります.
特に注意:元の関数がオブジェクトタイプを返すと、新しいオブジェクトを返すことができなくなり、thisにバインドされた新しいオブジェクトが失われます.例:
2.thisバインド優先度
プロセスは退屈なコードテストで、私は直接優先度を書きました.
3 . まとめ関数が .関数が である場合.関数がコンテキストオブジェクトの下で呼び出された場合 .そうでない場合は、デフォルトバインディング を使用します.
4 . 面接問題の解析
1.
-----------------------------------------------------------------------------このデフォルトバインド2.この隠しバインド
2.
---------------------------------------
答え:undefined 10解析:ポイント1.全局汚染2.デフォルトのバインド
この問題はとても面白くて、問題は基本的に第1 undefinedに集中して、これは実は問題の小さい罠ですが、スタックを追う過程は絶対にすばらしくて、私たちにここで何が起こったのかを一歩一歩分析させます. foo(1)実行は、デフォルトバインドであることがわかりやすいでしょう.これはwindowを指し、関数の中でwindowに等価です.a = 1,return window; var a=foo(1)はwindowに等しい.a=window、var aがwindowであることを無視する人が多い.a,与えられたばかりの1を置き換えた. だからここのaの値はwindowで、a.aもwindowで、つまりwindowです.a = window ; window.a.a = window; foo(10)は初めてと同じデフォルトバインドで、このときwindow.aは10に値をつけて、ここが肝心なことに注意して、もとはwindow.a=windowは、現在10に割り当てられ、値タイプになっているので、現在a.a=undefinedとなっています.(これを検証するにはvar b=foo(10)を必要とします.削除、ここのa.aはやはりwindow) var b = foo(10); Windowsに等価です.b = window;
本題のすべての変数の値、a=window.a = 10 , a.a = undefined , b = window , b.a = window.a = 10;
3.
------------------------------------------------------------------
4.圧巻の問題
------------------------------------------------------------------newバインド2.隠性バインド3.デフォルトのバインド4.へんすうおせん
5 . 矢印関数のthisバインド(2017.9.18更新)
矢印関数は、矢印関数は、上述した4つのバインドを使用するのではなく、外部の役割ドメインに完全に基づいてthisを決定します.(その親は私たちのルールを使っていますよ) 矢印関数のthisバインドは修正できません(この特性は非常に爽やかです) まずコードを見て強化します.
さあ、実戦で、前の最初の例を覚えていますか.矢印関数の形式に変更します(気持ち悪いthisバインドの問題を徹底的に解決できます):
=================================
なぜ矢印関数の外にもう1つセットして、直接書けばいいのか分からない人もいるかもしれませんが、こんなに面倒なことをして何をしているのか、実はこれも矢印関数の多くの人が使いにくいところです. objの現在の役割ドメインはwindow、例えばobjであることを明らかにしなければならない.that === window. function(functionには独自の関数役割ドメインがある)でラップしない場合、デフォルトでバインドされている親役割ドメインはwindowです. をfunctionで包む目的は、矢印関数を現在のオブジェクトにバインドすることです.関数の役割ドメインは現在のオブジェクトであり、矢印関数は関数が存在する役割ドメインのthis、すなわちobjを自動的にバインドします.
滋養に満ちて抜けてしまった
参考書:あなたが知らないJavaScriptKYLE SIMPSON著(おすすめ)
var people = {
name : " ",
getName : function(){
console.log(this.name);
}
};
window.onload = function(){
xxx.onclick = people.getName;
};
普段レンガを運ぶときによくあるthisバインドの問題は、xxxに書いたり出会ったりするかもしれません.onclickがトリガーされたとき、何を出力しますか?
テストを容易にするために、コードを簡略化します.
var people = {
Name: " ",
getName : function(){
console.log(this.Name);
}
};
var bar = people.getName;
bar(); // undefined
この小さな例を通して、
this
の気持ち悪いところを感じてみましょう.私が最初にこの問題に遭遇したときも愚かな顔をしていました.コードのthis
は作成時に非常に明らかな指向をしていましたから.自分のpeople
のオブジェクトを指していましたが、実際にはwindow
のペアを指していました.これは私がすぐに皆さんに言うthis
のバインドルールです.1 .
this
this
とは?this
バインドについて議論する前に、thisが何を表しているのかを明らかにしなければなりません.this
バインド規則以下に説明する4つのバインドのルールを把握すると、関数呼び出しを見るだけで
this
の指向を判断することができます.2.1デフォルトバインド
次のコードを考慮します.
function foo(){
var a = 1 ;
console.log(this.a); // 10
}
var a = 10;
foo();
これが典型的なデフォルトバインドです.foo呼び出しの位置を見てみましょう.「ライトレバー司令官」というように、修飾されていない関数呼び出しを直接使用すると、デフォルトでデフォルトバインドしか適用できません.
では、デフォルトはどこにバインドされていますか.一般的には
window
で、厳格なモードではundefined
です.2.隠性バインディング
コード:
function foo(){
console.log(this.a);
}
var obj = {
a : 10,
foo : foo
}
foo(); // ?
obj.foo(); // ?
答え:undefined 10
foo()
のこの書き方はよく知っていますか.私たちが書いたばかりのデフォルトのバインドは、window.a
の印刷に等しいので、undefined
を出力します.次のobj.foo()
はよく書くべきです.これは実は私たちがすぐに議論する隠れたバインドです.関数fooが実行されると、コンテキストオブジェクト、すなわち
obj
が存在する.この場合、関数のthisはコンテキストオブジェクトにデフォルトでバインドされ、印刷obj.a
に等価であるため、10
が出力される.チェーン性の関係、例えば
xx.yy.obj.foo();
であれば、コンテキストは関数の直接的な上位、すなわち隣接するもの、あるいはオブジェクトチェーンの最後のものを取ります.2.3顕性バインディング
2.3.1暗黙的バインドの制限
私たちのさっきの暗黙的なバインドには致命的な制限があります.コンテキストには私たちの関数が含まれなければなりません.例:
var obj = { foo : foo }
です.コンテキストに私たちの関数が含まれていない場合は、暗黙的なバインドで明らかにエラーが発生します.すべてのオブジェクトにこの関数を追加することはできません.そうすると、拡張性が悪くなります.メンテナンス性が悪いです.次にお話しするのは、関数にthisを強制的にバインドすることです.2 .3 .2 call apply bind
ここではjsが提供する関数callとapplyを使用します.これらの役割は関数のthis指向を変更し、最初のパラメータはthisオブジェクトを設定することです.
2つの関数の違い:
例:
function foo(a,b){
console.log(a+b);
}
foo.call(null,' ',' '); // this null
foo.apply(null, [' ',' '] ); //
call,apply関数のほかにthisを変える関数bindがあり,call,applyとは異なる.
bindには1つの関数しかなく、すぐに実行されません.ただ、1つの値を関数のthisにバインドし、バインドされた関数を返します.例:
function foo(){
console.log(this.a);
}
var obj = { a : 10 };
foo = foo.bind(obj);
foo(); // 10
(bind関数はとても特別で、今度みんなと一緒にそのソースコードを議論します)
2.3.2顕性バインド
本題を開始し、コードを上げると、上の暗黙的なバインドの例を使用します.
function foo(){
console.log(this.a);
}
var obj = {
a : 10 // foo
}
foo.call(obj); // 10
暗黙的なバインド例のコンテキストオブジェクトの関数を削除しました.明らかに
.
という形式で関数を呼び出すことはできません.コードの明示的なバインドコードfoo.call(obj)
を見てください.変に見えますが、私たちが以前知っていた関数呼び出しとは違います.実はcallはfoo上の関数で、thisの指向を変えながらこの関数を実行します.
(深く理解したい[
call apply bind this , ,
]など、より多くのブラックテクノロジーのパートナーは私や本稿のコメントに注目してください.最近、私は単独で1期を作って一緒に文章を書きます)(見たくないパートナーは心配しないで、本稿の理解に影響しません)2.4 newバインド
2.4.1
new
とはオブジェクト向けを学んだパートナーはnewに慣れていないに違いない.jsのnewと伝統的なオブジェクト向け言語のnewの役割は新しいオブジェクトを作成することだが、彼らのメカニズムはまったく異なる.
新しいオブジェクトを作成するには、
という概念が欠かせません.従来のオブジェクト向けコンストラクション関数はクラス内の特殊な関数であり、オブジェクトを作成するときにnew ()
の形式でクラス内のコンストラクション関数を呼び出しますが、jsでは異なります.jsにおけるnewで修飾された関数は「構造関数」であり、正確には関数の
である.jsにはいわゆる「構造関数」は存在しないからである.ではnewで関数の
を作った後、jsは私たちにどんな仕事をしてくれましたか.__proto__
属性を元の関数のprototype
属性に向けます.(すなわち、元の関数を継承するプロトタイプ)3つ目は次のnewバインドです
2.4.2 newバインド
ピッピッピッピッピッピッピッピッピッピッピッピッピッピッピッピッピッピッピッピッピッピッピッピッピッ
function foo(){
this.a = 10;
console.log(this);
}
foo(); // window
console.log(window.a); // 10
var obj = new foo(); // foo{ a : 10 }
// foo { a : 10 }; var obj = foo;
console.log(obj.a); // 10 new
newを使用して関数を呼び出すと、関数は独自の名前で名前を付けて新しいオブジェクトを作成し、戻ります.
特に注意:元の関数がオブジェクトタイプを返すと、新しいオブジェクトを返すことができなくなり、thisにバインドされた新しいオブジェクトが失われます.例:
function foo(){
this.a = 10;
return new String(" ");
}
var obj = new foo();
console.log(obj.a); // undefined
console.log(obj); // " "
2.thisバインド優先度
プロセスは退屈なコードテストで、私は直接優先度を書きました.
new > > >
3 . まとめ
new
によって修飾する場合 this , :var bar = new foo(); foo this foo , bar , new .
call,apply,bind
を使用して呼び出す this call,apply,bind . : foo.call(obj); , foo this obj , .
this , : var obj = { foo : foo }; obj.foo(); foo this obj . .
:function foo(){...} foo() ,foo this window.( undefined).
.
4 . 面接問題の解析
1.
var x = 10;
var obj = {
x: 20,
f: function(){
console.log(this.x); // ?
var foo = function(){
console.log(this.x);
}
foo(); // ?
}
};
obj.f();
-----------------------------------------------------------------------------このデフォルトバインド2.この隠しバインド
var x = 10;
var obj = {
x: 20,
f: function(){
console.log(this.x); // 20
// , f this obj , 20
function foo(){
console.log(this.x);
}
foo(); // 10
// foo f , f ,
// this obj , this ,
// ' ', , this window
}
};
obj.f();
2.
function foo(arg){
this.a = arg;
return this
};
var a = foo(1);
var b = foo(10);
console.log(a.a); // ?
console.log(b.a); // ?
---------------------------------------
答え:undefined 10解析:ポイント1.全局汚染2.デフォルトのバインド
この問題はとても面白くて、問題は基本的に第1 undefinedに集中して、これは実は問題の小さい罠ですが、スタックを追う過程は絶対にすばらしくて、私たちにここで何が起こったのかを一歩一歩分析させます.
本題のすべての変数の値、a=window.a = 10 , a.a = undefined , b = window , b.a = window.a = 10;
3.
var x = 10;
var obj = {
x: 20,
f: function(){ console.log(this.x); }
};
var bar = obj.f;
var obj2 = {
x: 30,
f: obj.f
}
obj.f();
bar();
obj2.f();
------------------------------------------------------------------
var x = 10;
var obj = {
x: 20,
f: function(){ console.log(this.x); }
};
var bar = obj.f;
var obj2 = {
x: 30,
f: obj.f
}
obj.f(); // 20
// ,this obj,
bar(); // 10
//' ' ( obj.f )
obj2.f(); //30
// f ,this ,
4.圧巻の問題
function foo() {
getName = function () { console.log (1); };
return this;
}
foo.getName = function () { console.log(2);};
foo.prototype.getName = function () { console.log(3);};
var getName = function () { console.log(4);};
function getName () { console.log(5);}
foo.getName (); // ?
getName (); // ?
foo().getName (); // ?
getName (); // ?
new foo.getName (); // ?
new foo().getName (); // ?
new new foo().getName (); // ?
------------------------------------------------------------------newバインド2.隠性バインド3.デフォルトのバインド4.へんすうおせん
function foo() {
getName = function () { console.log (1); };
// getName window
return this;
}
foo.getName = function () { console.log(2);};
// getName , foo
foo.prototype.getName = function () { console.log(3);};
// getName foo , new
var getName = function () { console.log(4);};
// foo getName , window
function getName () { console.log(5);}
// , , ,
// , getName(), ,
// ,
// getName
foo.getName (); // 2
// , getName
// 2 3 , 3 ,
// foo.prototype , ,
// , foo.prototype.getName() ,
// 3 2, ( new ,
// Prototype , new )
getName (); // 4
// , 5 4 ,
// 5 4 , js ,
//
foo().getName (); // 1
// foo , 1. window.getName 1,
// 2. window , window.getName(); 1
getName (); // 1
// window.getName 1, 1
new foo.getName (); // 2
// new , foo.getName ,
// , , 2 (
// , foo.getName
// __proto__ getName , 3 )
new foo().getName (); // 3
// ,new ,
// ,foo(), , var obj = new foo();
// obj.getName(); , prototype
// getName 3 , new prototype
new new foo().getName (); // 3
// , , :
// var obj = new foo();
// var obj1 = new obj.getName();
// , , ,obj getName 3, 3
// obj foo ,obj1 obj.getName
5 . 矢印関数のthisバインド(2017.9.18更新)
矢印関数は、
function
のキーワードではなく、=>
、学名
(2333)を使用します.通常の関数とは異なります.function foo(){
return ()=>{
console.log(this.a);
}
}
foo.a = 10;
// 1. this
var bar = foo(); // foo
bar(); // undefined ,
var baz = foo.call(foo); // foo
baz(); // 10
// 2. this
// foo baz
var obj = {
a : 999
}
baz.call(obj); // 10
さあ、実戦で、前の最初の例を覚えていますか.矢印関数の形式に変更します(気持ち悪いthisバインドの問題を徹底的に解決できます):
var people = {
Name: " ",
getName : function(){
console.log(this.Name);
}
};
var bar = people.getName;
bar(); // undefined
=================================
var people = {
Name: " ",
getName : function(){
return ()=>{
console.log(this.Name);
}
}
};
var bar = people.getName(); // people , this , ?
bar(); //
なぜ矢印関数の外にもう1つセットして、直接書けばいいのか分からない人もいるかもしれませんが、こんなに面倒なことをして何をしているのか、実はこれも矢印関数の多くの人が使いにくいところです.
var obj= {
that : this,
bar : function(){
return ()=>{
console.log(this);
}
},
baz : ()=>{
console.log(this);
}
}
console.log(obj.that); // window
obj.bar()(); // obj
obj.baz(); // window
滋養に満ちて抜けてしまった
参考書:あなたが知らないJavaScriptKYLE SIMPSON著(おすすめ)