1. 程式人生 > 程式設計 >js this 繫結機制深入詳解

js this 繫結機制深入詳解

本文例項講述了js this 繫結機制。分享給大家供大家參考,具體如下:

函式呼叫位置

與詞法作用域相反的是,this的指向由函式執行時決定,它是動態的,隨著函式呼叫位置變化而變化。

要理解 this,首先要理解呼叫位置:呼叫位置就是函式在程式碼中被呼叫的位置(而不是宣告的位置)。只有仔細分析呼叫位置才能回答這個問題:這個this到底引用的是什麼?

function baz() {
// 當前呼叫棧是:baz
// 因此,當前呼叫位置是全域性作用域
console.log( "baz" );
bar(); // <-- bar的呼叫位置
}
function bar() {
// 當前呼叫棧是baz -> bar
// 因此,當前呼叫位置在baz中
console.log( "bar" );
foo(); // <-- foo的呼叫位置
}
function foo() {
// 當前呼叫棧是baz -> bar -> foo
// 因此,當前呼叫位置在bar中
console.log( "foo" );
}
baz(); // <-- baz的呼叫位置

多數現代桌面瀏覽器都內建了開發者工具,其中包含JavaScript偵錯程式。你可以在工具中給函式的第一行程式碼設定一個斷點,或者直接在第一行程式碼之前插入一條 debugger;語句。執行程式碼時,偵錯程式會在那個位置暫停,同時會展示當前位置的函式呼叫列表,這就是你的呼叫棧。因此,如果你想要分析this的繫結,使用開發者工具得到呼叫棧,然後找到棧中第二個元素,這就是真正的呼叫位置。

this 繫結規則

函式的呼叫位置決定了this的繫結物件,當我們找到呼叫位置後,然後判斷需要應用下面四條規則中的哪一條。

獨立函式呼叫

獨立函式呼叫,this 指向函式呼叫位置所在的包含環境物件。

function foo() {
console.log( this.a );
}
var a = 2;
foo(); // 2

作為物件的方法呼叫

當函式作為某個物件的方法被呼叫時,this 指向這個物件。

function foo() {
console.log( this.a );
}
var obj = {
a: 2,foo: foo
};
obj.foo(); // 2

特別注意:雖然函式foo並不屬於obj物件,但呼叫位置使用obj的上下文來呼叫函式。我一直在強調呼叫位置的重要性,因為你可能一不留神就會忽略掉它,看下面的列子:

function foo() {
console.log( this.a );
}
var obj = {
a: 2,foo: foo
};
var bar = obj.foo; // 函式別名! 步驟1
var a = "oops,global"; // a是全域性物件的屬性
bar(); // "oops,global" 步驟2

在步驟1中,變數bar是obj.foo 的一個引用,它實際指向的是函式foo。所以使用bar()與直接使用foo()並沒有不同。

使用 .call/ .apply 繫結

每建立一個函式,這個函式就有了兩個繼承而來的方法:call和apply。

它們的第一個引數是一個物件,它們會把這個物件繫結到this,接著在呼叫函式時指定這個 this 。因為你可以直接指定 this 的繫結物件,因此我們稱之為顯式繫結。

function foo() {
console.log( this.a );
}
var obj = {
a:2
};
foo.call( obj ); // 2

new繫結

使用 new 來呼叫函式,或者說發生建構函式呼叫時,會自動執行下面的操作。

  1. 建立(或者說構造)一個全新的物件。
  2. 這個新物件會被執行[[原型]]連線,即指向建構函式的原型Foo.prototype。
  3. 這個新物件會繫結到函式呼叫的 this 。
  4. 如果函式沒有返回其他物件,那麼 new 表示式中的函式呼叫會自動返回這個新物件。
function foo(a) {
this.a = a;
}
var bar = new foo(2);
console.log( bar.a ); // 2

使用 new 來呼叫 foo(..) 時,我們會構造一個新物件並把它繫結到 foo(..) 呼叫中的 this 上。

優先順序

如果要判斷一個執行中函式的this繫結,就需要找到這個函式的直接呼叫位置。找到之後就可以順序應用下面這四條規則來判斷 this 的繫結物件。

  1. 由 new 呼叫?繫結到新建立的物件。
  2. 由 call 或者 apply (或者 bind )呼叫?繫結到指定的物件。
  3. 由上下文物件呼叫?繫結到那個上下文物件。
  4. 預設:在嚴格模式下繫結到 undefined ,否則繫結到全域性物件。

一定要注意,有些呼叫可能在無意中使用預設繫結規則。如果想“更安全”地忽略 this 繫結,你可以使用一個DMZ物件,比如 ø = Object.create(null) ,以保護全域性物件。

感興趣的朋友可以使用線上HTML/CSS/JavaScript程式碼執行工具:http://tools.jb51.net/code/HtmlJsRun測試上述程式碼執行效果。

更多關於JavaScript相關內容可檢視本站專題:《JavaScript常用函式技巧彙總》、《javascript面向物件入門教程》、《JavaScript錯誤與除錯技巧總結》、《JavaScript資料結構與演算法技巧總結》及《JavaScript數學運算用法總結》

希望本文所述對大家JavaScript程式設計有所幫助。