前排聲明,這真的是寫 this,沒有什麼太多新的東西,就是一個本身對 this 綁定規則的總結,也許後期水平提升會從更深的角度去解釋 JavaScript 中的 this 綁定規則。本文從分別從綁定全局對象和綁定具體對象的角度總結了一下 this 的綁定規則。javascript
this 綁定全局對象,分爲兩種狀況:html
這種沒什麼好說的,全局做用域下調用 this 綁定的是全局對象,例如瀏覽器中綁定的是 window 對象。java
console.log(this)
複製代碼
直接拿上面這段代碼在瀏覽器控制檯運行下獲得的就是全局對象。數組
直接使用函數名調用函數瀏覽器
function test () {
console.log(this); // window
}
test();
var test1 = function() {
console.log(this); // window
}
test1();
複製代碼
代碼傳送門app
定義某對象的屬性爲函數,該屬性值被賦值被另外一變量時。dom
var aObj = {
propertyFn: function() {
console.log(this.testName);
},
testName: "aObj"
};
aObj.propertyFn(); // "aObj"
var aFn = aObj.propertyFn;
aFn(); // undefined
複製代碼
代碼傳送門函數
綁定具體對象的狀況較多,可總結爲如下幾種ui
函數做爲對象屬性被獲取並調用時,函數中的 this 綁定的是獲取該屬性的對象。考慮以下代碼this
function test() {
return this.testName;
}
var testObj = {
testName: "testObj",
getTestName: test,
getTestNameFn: function() {
return this.testName;
}
}
console.log(testObj.getTestName()); // "testObj" 函數雖然是在全局做用域下定義的,
// 可是被賦值給了testObj的getTestName屬性,且是被做爲對象的屬性調用的
console.log(testObj.getTestNameFn()); // "testObj"
複製代碼
使用 new 關鍵字調用函數時,該函數會生成並返回以個新的對象,函數中的 this 綁定的生成的新對象。
function Test(name) {
this.name = name;
console.log(this);
}
var s = new Test("s"); // {name: "s"}
function Test1(name) {
this.name = name;
console.log(this);
return true;
}
var s1 = new Test1("s1"); // {name: "s1"}
function Test2(name) {
this.name = name;
console.log(this);
return {};
}
var s2 = new Test2("s2"); // {name: "s2"}
複製代碼
上述結果代表,使用 new 調用函數的時候必定會生成一個新對象,且 this 綁定的就是這個新對象,只不過當你在函數中 return 了非 Object 類型的值時,這個對象不會被賦值給你定義的接收變量,這時接收的變量被賦的是函數中使用 return 返回的值。
這裏call和apply的做用是相似的,都是函數的實例方法,可爲函數指定 this 綁定的對象,二者區別在於 apply 的第二個參數是數組,該數組中的值會以實參形式被傳遞給調用 apply 的函數,而 call 函數除了第一個參數外的參數均被傳遞給調用 call 的函數。
function test(param1, param2) {
console.log(this.name, param1 + ", " + param2);
}
var a = {
name: "a"
};
var b = {
name: "b"
}
test.call(a, "aParam1", "bParam2");
test.apply(b, ["bParam1", "bParam2"]);
test();
複製代碼
bind函數的做用和以上二者與別很大,其做用是將函數中的 this 綁定對象與指定的對象綁定起來,返回一個函數,每次調用返回的函數時,其 this 都是綁定的指定對象。
function test() {
console.log(this.name);
}
var a = {
name: "a"
}
var bindTest = test.bind(a);
bindTest(); // "a"
var b = {
name: "b",
getName: bindTest
}
b.getName(); // "a"
var c = new bindTest(); // undefined
bindTest.call(b); // "a"
複製代碼
從上面的示例代碼能夠看出,函數和指定對象被綁定後使用 new 關鍵字是綁定失效,在以上示例中綁定函數中的 this 綁定的是一個新建立的對象實例,且該對想的構造函數時test函數。由此也可得出,this 綁定場景同時出現的狀況下 new 的優先級是高於調用 bind 函數的優先級的。
關於箭頭函數中 this 綁定,MDN 中的說法是箭頭函數是沒有本身的 this 的,其 this 是從其做用域鏈上層做用域繼承而來的。那麼怎麼理解呢?下面上代碼:
let arrowFn = () => {console.log(this === window)};
arrowFn(); // true
let a = {
name: "a",
getSelf: arrowFn
};
a.getSelf(); // true
let b = {
name: "b"
}
arrowFn.call(b); // true
複製代碼
以上代碼是箭頭函數直接在全局做用域下定義的狀況,那麼其做用域鏈上層就是全局做用域,而在瀏覽器中全局做用域 this 綁定的值是 window 。因爲 JavaScript 中的做用域是靜態做用域,那麼箭頭函數在全局做用域中定義時便已經能夠肯定其 this 就是 window 了,並且後面的該箭頭函數做爲對象屬性值被調用,仍是使用 call 顯示指定 this 其 this 均爲改變。而非箭頭函數的畫風是這樣的:
let fn = function() {
console.log(this === window);
}
fn(); // true
let a = {
name: "a",
getSelf: fn
};
a.getSelf(); // false
let b = {
name: "b"
}
fn.call(b); // false
複製代碼
那麼是否是一旦箭頭函數被定義了,其 this 的綁定就已經被肯定了呢?
let createArrowFn = function() {
return () => {console.log(this)};
}
let a = {
name: "a",
getSelf: createArrowFn
};
let aArrow = a.getSelf();
aArrow(); // 對象a
let b = {
name: "b"
}
var bArrow = createArrowFn.call(b);
bArrow(); // {name: "b"}
複製代碼
上面代碼兩次 this 打印的結果是不同的,那麼是否是就推翻了箭頭函數一旦被定義,其 this 就已經肯定了的結論。其實否則,這裏箭頭函數的是上層做用域是createArrowFn這個函數的做用域,這個函數做用域中的 this 會隨着調用場景的不一樣發生發生變化,因此繼承其做用域綁定 this 的箭頭函數中的 this 天然也會發生改變了。其實箭頭函數中的 this 能夠這麼理解,至關於將函數的上層做用域的 this 用一個變量保存下來,而後在其子函數中使用它。
let fn = function() {
var _this = this;
return function() {
console.log(_this);
}
}
let a = {
name: "a",
getSelf: createArrowFn
};
let aArrow = a.getSelf();
aArrow(); // 對象a
let b = {
name: "b"
}
var bArrow = createArrowFn.call(b);
bArrow(); // {name: "b"}
複製代碼
在理解箭頭函數中的 this 時只需理解其被定義時所在做用域的 this 綁定的是什麼就能夠了。
先說明不論是事件監聽器仍是事件處理器中 this 綁定的都是當前觸發該事件的節點,即綁定了該事件的元素節點。
這裏主要是區分下事件監聽器和事件處理器,事件處理器實際上是指 html 標籤的 on... 屬性定義的函數,好比 onclick="function() {}",固然也能夠在 JavaScript 中去設置該屬性,事件處理器的特色是其只能有一個,由於是 html 標籤屬性因此能夠覆蓋。
事件監聽器是指使用addEventListener函數註冊的事件回調函數,可同時註冊多個。
在討論 JavaScript 中 this 的綁定值時,其實就是幾種狀況:
以上總結僅僅爲粗淺的不一樣場景下 this 的綁定值的總結,沒有從更深的層次(好比ES標準中的定義)去討論,主要我的
時間水平有限,因此大佬請忽略。若有錯誤歡迎各位指正,不勝感激。