DIV CSS 佈局教程網

 DIV+CSS佈局教程網 >> 網頁腳本 >> JavaScript入門知識 >> 關於JavaScript >> JavaScript中this的四個綁定規則總結
JavaScript中this的四個綁定規則總結
編輯:關於JavaScript     

前言

如果要問javascript中哪兩個知識點容易混淆,作用域查詢和this機制絕對名列前茅。所以這篇文章開始將介紹javascript中this的四個綁定規則,下面來一起看看吧。

綁定規則

1. 默認綁定

獨立函數調用時,this 指向全局對象,如果使用嚴格模式,那麼全局對象無法使用默認綁定, this綁定至 undefined

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

嚴格模式時:

function foo() {
 "use strict";
 console,log(this.a);
}
var a = 2;
foo(); // TypeError: this is undefined

2. 隱式綁定

當函數引用有上下文對象時(即函數作為引用屬性被添加到對象中),隱式綁定規則會把函數調用中的 this 綁定到這個上下文對象。

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

對象屬性引用鏈中只有最頂層或者說最後一層會影響調用位置:

obj1.obj2.foo(); // foo 中的 this 與 obj2 綁定

2.1 隱式丟失

隱式丟失指的是函數中的 this 丟失綁定對象,即它會應用第 1 條的默認綁定規則,從而將 this 綁定到全局對象或者 undefined 上,取決於是否在嚴格模式下運行。以下情況會發生隱式丟失:

綁定至上下文對象的函數被賦值給一個新的函數,然後調用這個新的函數時:

function foo() {
 console.log( this.a);
}
var obj = {
 a: 2,
 foo: foo
};
var bar = obj.foo; //函數別名
var a = "這是全局變量喔";
bar(); // "這是全局變量喔"

傳入回調函數時:

function foo() {
 console.log( this.a);
}
function doFoo(fn) {
 fn(); // <-- 調用位置
}
var obj = {
 a: 2,
 foo: foo
};
var a = "這是全局變量喔";
doFoo( obj.foo ); // "這是全局變量喔"

其實這就是第一種情況的變種,實際上參數傳遞就是一種隱式賦值。除了開發人員自定義的函數,在將函數傳入語言內置的函數比如 setTimeout 時,同樣會發生隱式丟失的情況。

3. 顯式綁定

顯式綁定的核心是 JavaScript 內置的 call(..) apply(..) 方法,這兩個方法在 JavaScript 提供的絕大多數函數以及開發者自己創建的所有函數上都可以使用。

call(..) apply(..)的第一個參數是一個對象(二者區別在後面傳入的參數形式,這裡不是重點,不討論),他們會將 this 綁定到這個對象上。因為你可以直接指定 this 綁定的對象,所以這條規則被稱為顯式綁定。

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

如果 call 或者 apply 傳入的第一個參數是原始值(字符串類型、布爾類型或者數字類型),那麼該原始值會被轉換成它的對象形式(new String()new Boolean() new Number() ),俗稱“裝箱”。

顯式綁定仍然無法解決丟失綁定問題。

3.1 硬綁定

作為顯式綁定的一個變種,硬綁定可以解決丟失綁定問題。

function foo() {
 console.log( this.a);
}
var obj = {
 a: 2
};
var bar = function() {
 foo.call(obj);
};
bar(); // 2
setTimeout(bar, 100); // 2
bar.call(window); //無效,硬綁定的 bar 不會再修改它的 this

在一個新的函數內部強制綁定 this 到某個對象上,無論之後如何調用這個新的函數,其 this 都不會丟失。

典型應用場景為創建一個包裹函數,傳入所有的參數並返回接收到的所有值:

function foo(something) {
 console.log(this.a, something);
 return this.a + something;
}
var obj = {
 a:2
};
var bar = function() {
 return foo.call(obj, arguments);
};
var b = bar(3); // 2 3
console.log(b); // 5

或者將綁定的對象改為可配置,這樣就成了一個輔助綁定函數:

...
function bind(fn, obj){
 return function(){
 return fn.apply(obj, arguments);
 };
}
...

由於硬綁定實在太過常見,所以 ES5 提供了內置的 Function.prototype.bind,其用法如下:

function foo(something) {
 console.log(this.a, something);
 return this.a + something;
}
var obj = {
 a: 2
};
var bar = foo.bind(obj);
var b = bar(3); // 2 3
console.log(b); // 5

3.2 API 調用的“上下文”

JavaScript 自身以及許多第三方庫的函數都提供了一個可選的參數,通常被稱為“上下文”,其作用和 bind(..) 一樣,確保回調函數使用指定的 this

function foo(el) {
 console.log( el, this.id);
}
var obj = {
 id: "awsome"
};
//調用 foo(..) 時把 this 綁定到 obj
[1,2,3].forEach(foo, obj);
// 1 awsome 2 awsome 3 awsome

實際上這些函數背後還是調用了 call() 或者 apply() ,只不過這樣開發者需要寫的代碼就少了一些。

4. new 綁定

使用 new 來調用函數時,會自動執行下面的操作:

     1、創建一個全新的對象

     2、這個新對象會被執行 [[原型]] 連接

     3、這個新對象會綁定到函數調用的 this

     4、如果函數沒有返回其他對象,那麼 new 表達式中的函數調用會自動返回這個新對象

舉例如下:

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

使用 new 來調用 foo(..) 時,會構造一個新對象並把它綁定到 foo(..) 調用中的 this 上。

優先級

具體推斷細節不再表述,以上四種綁定規則的使用先後推斷如下:

1、函數是否在 new 中調用(new 綁定)?如果是的話 this 綁定的是新創建的對象。

var bar = new foo();

2、函數是否通過 callapply (顯示綁定)或者硬綁定?如果是的話,this 綁定的是指定的對象。

var bar = foo.call(obj2);

3、函數是否在某個上下文對象中調用(隱式綁定)?如果是的話,this 綁定的是那個上下文對象。

var bar = obj1.foo();

4、如果都不是的話,使用默認綁定。如果在嚴格模式下,就綁定到 undefined,否則綁定到全局對象。

var bar = foo();

綁定例外

如果把 null 或者 undefined 作為 this 的綁定對象傳入 callapply 或者 bind,那麼這些值在調用時會被忽略,實際應用的是默認綁定規則。(書中推薦使用一個空對象來綁定 this)。

間接引用。這種情況容易在賦值時發生:

function foo() {
 console.log( this.a);
}
var a = 2;
var o = {a: 3, foo: foo};
var p = {a: 4};
o.foo(); // 3
(p.foo = o.foo)(); // 2

p.foo() 實際上引用了 foo() ,如此,會應用默認綁定。

另外ES6 對改變 this 的混亂綁定作了相應的努力,誕生了箭頭函數,其根據當前詞法作用域來決定 this 而非上面的四條規則,具體來說,箭頭函數會繼承外層函數調用的 this 綁定(這其實和 ES6 之前代碼中的 self = this 是一個道理)。

總結

以上就是這篇文章的全部內容,希望能對大家的學習或者工作帶來一定的幫助,如果有疑問大家可以留言交流。

XML學習教程| jQuery入門知識| AJAX入門| Dreamweaver教程| Fireworks入門知識| SEO技巧| SEO優化集錦|
Copyright © DIV+CSS佈局教程網 All Rights Reserved