this - 想說愛你不容易
前言
javascript中的this是啥東西?為啥我們經常被他搞得暈頭轉向不知所以?他是惡魔?是天使 ?是怪胎?讓我們一起來揭開它那神秘的面紗。
他是個啥
首先 this 是Javascript語言的關鍵字之一,指函數 運行 時的當前對象。那既然和函數運行有關,js中函數有哪些調用模式呢?
-
純粹的函數調用
-
對象的方法調用
-
構造函數調用
-
apply、call調用
我擦,有木有一千只草泥馬在心里蹦騰不息,人家是要弄懂this,你這又是整的哪一出
我們慢慢來,一步步從這些調用模式中探究this這個神奇的遠古神獸
純粹的函數調用
函數調用 即 functionName () 模式,這也是我們使用的最多的一種方式,其屬于全局調用,瀏覽中默認情況下函數內部的this指向 window ,當然是在非嚴格模式下。
this.name = 'qianlong';
function showName () {
console.log(this.name);
console.log(this === window);
}
showName()
// qianlong
// true</code></pre>
對象的方法調用
當一個函數作為對象的某個屬性方法被調用的時候
var obj = {
name: 'qianlong',
showName: function () {
console.log(this.name);
}
};
obj.showName();
// qianlong</code></pre>
可以看出 this 指向是obj這個對象,其實本質上講函數調用形式內部 this 就是指向調用它的那個對象
上面的例子相當于
window.showName()
這也是為什么可以讀取到全局定義的name屬性的原因。
再來
var showName = function () {
console.log(this.name);
},
obj = {
name: 'qianlong',
showName: showName
};
obj.showName();</code></pre>
這個時候輸出的是什么呢
結果是不變的,在js中,一切都是對象,而這里也只是將,obj的showName屬性指向,showNmae函數的引用地址。
繼續
當我們把 showName 方法賦值給了一個變量,又會有什么事情發生呢?
var obj = {
name: 'qianlong',
showName: function () {
console.log(this.name);
}
};
var tempShowName = obj.showName;
tempShowName()
// undefined</code></pre>
為什么不是期望的那樣輸出 qianlong呢。obj的showName方法是一個對象,當把它賦值給了tempShowName變量,此時便和obj沒有什么關系了,而這個時候的調用和下面是等價的。
window.tempShowName()
window上此事并沒有 name 屬性,自然輸出是 undefined 。
構造函數調用
當使用 new 去調用一個構造函數的時候,內部的this,指向的是實例化出來的對象。
var Person = function (name, sex) {
this.name = name;
this.sex = sex;
console.log(this);
};
var p1 = new Person('qianlong', 'boy');
// Person {name: 'qianlong', sex: 'boy'};</code></pre>
構造函數也是函數,所以當你用普通調用方式調用時
var Person = function (name, sex) {
this.name = name;
this.sex = sex;
console.log(this);
};
Person('qianlong', 'boy');
// 這個時候相當于給window對象添加了name和sex兩個屬性。
window.name // 'qianlong'
window.sex // 'boy'</code></pre>
apply、call調用
使用call和apply方式去調用一個函數的時候,內部的this指向的是傳進來的第一個參數,當第一個參數是 undefined 或者 null 的時候,依舊指向 window
var showName = function () {
console.log(this);
};
showName() // window
showName.call(undefined) // window
showName.call(null) // window
showName.call({name: 'qianlong'}) // {name: 'qianlong'}
箭頭函數
在 ES6 的新規范中,加入了箭頭函數,它和普通函數最不一樣的一點就是 this 的指向,普通函數中的this,是運行時候決定的,而箭頭函數卻是定義時候就決定了。
var obj = {
name: 'qianlong',
showName: function () {
console.log(this.name);
},
showNameLater: function () {
setTimeout(() => {
console.log(this.name);
}, 1000)
}
};
obj.showNameLater();
// qianlong</code></pre>
var obj = {
name: 'qianlong',
showName: () => {
console.log(this.name);
}
};
obj.showName();
// undefined</code></pre>
一些坑
1. setTimeout
var obj = {
name: 'qianlong',
showName: function () {
console.log(this.name);
},
showNameLater: function () {
setTimeout(this.showName, 1000);
}
};
obj.showNameLater();
// undefined</code></pre>
這里在執行setTimeout這個函數的時候傳了obj的showName函數作為第一個參數,其效果與
var showName = obj.showName
是相同的。而setTimeout內部其實也是執行了傳進去這個函數而已,即。
showName();
還記得這種調用方式和 window.showName() 是類似的效果嗎?這個時候輸入為undefined也就好理解了。
那么怎么解決這個問題呢,畢竟我們期望的效果是輸出 qianlong 。
var obj = {
name: 'qianlong',
showName: function () {
console.log(this.name);
},
showNameLater: function () {
var self = this;
setTimeout(function () {
self.showName();
}, 1000);
}
};
obj.showNameLater();</code></pre>
或者
var obj = {
name: 'qianlong',
showName: function () {
console.log(this.name);
},
showNameLater: function () {
setTimeout(this.showName.bind(this), 1000);
}
};
obj.showNameLater();</code></pre>
2. setTimeout
尼瑪坑爹啊,居然還是因為你。
'use strict';
function show() {
console.log(this);
}
show(); // undefined
setTimeout(show, 1); // window</code></pre>
在嚴格模式下面,函數調用的時候沒有指定this的情況下,內部this的表現為 undefined ,但是setTimeout卻不同,其內部默認還是指向window。
3. 為構造函數指定this
var Person = function (name, sex) {
this.name = name;
this.sex = sex;
};
var p1 = new Person.call({});
// Uncaught TypeError: Person.call is not a constructor</code></pre>
這里報錯了,原因是我們去 new 了 Person.call 函數 ,這里的函數不是一個構造函數;
當然解決方式也是有的。
var Person = function (name, sex) {
this.name = name;
this.sex = sex;
};
var p1 = new (Person.bind({}))('qianlong', 'sex');
// Person {name: "qianlong", sex: "sex"}</code></pre>
4. 為箭頭函數指定this
var show = (str) => {
console.log(str);
console.log(this);
};
show('qianlong');
// qianlong
// window
show.call({name: 'qianlong'}, 'qianlong');
// qianlong
// window</code></pre>
可以看到使用call來手動改變箭頭函數中的this的時候,無法成功。 箭頭函數中的 this 在定義它的時候已經決定了(執行定義它的作用域中的 this),與如何調用以及在哪里調用它無關,包括 (call, apply, bind) 等操作都無法改變它的 this。
結語
文章可能有些疏漏與錯誤之處,歡迎各位指正。
來自:https://segmentfault.com/a/1190000007983078