this是什么
- 有兩種常見(jiàn)的對(duì)this的錯(cuò)誤理解:
1.this指向函數(shù)自身
2.this指向的是作用域 - 正確的理解
當(dāng)一個(gè)函數(shù)被調(diào)用時(shí)级解,會(huì)創(chuàng)建一個(gè)活動(dòng)記錄(有時(shí)候也稱(chēng)為執(zhí)行上下文)喊积。這個(gè)記錄會(huì)包含函數(shù)在哪里被調(diào)用(調(diào)用棧)殖告、函數(shù)的調(diào)用方式吃溅、傳入的參數(shù)等信息风罩。this 就是這個(gè)記錄的一個(gè)屬性陨亡,會(huì)在函數(shù)執(zhí)行的過(guò)程中用到岁忘。
簡(jiǎn)單來(lái)說(shuō)辛慰,this 的綁定和函數(shù)聲明的位置沒(méi)有任何關(guān)系,只取決于函數(shù)的調(diào)用方式干像。
綁定規(guī)則
1.默認(rèn)綁定(嚴(yán)格模式/非嚴(yán)格模式)
獨(dú)立函數(shù)調(diào)用帅腌,this指向全局對(duì)象。嚴(yán)格模式下蝠筑,不能講全局對(duì)象用于默認(rèn)綁定狞膘,this會(huì)綁定到undefined揩懒。在嚴(yán)格模式下調(diào)用函數(shù)則不影響默認(rèn)綁定什乙。
function foo() { // 運(yùn)行在嚴(yán)格模式下,this會(huì)綁定到undefined
"use strict";
console.log( this.a );
}
var a = 2;
// 調(diào)用
foo(); // TypeError: Cannot read property 'a' of undefined
function foo() { // 運(yùn)行
console.log( this.a );
}
var a = 2;
(function() { // 嚴(yán)格模式下調(diào)用函數(shù)則不影響默認(rèn)綁定
"use strict";
foo(); // 2
})();
2.隱式綁定
當(dāng)函數(shù)引用有上下文對(duì)象時(shí)已球,隱式綁定規(guī)則會(huì)把函數(shù)中的this綁定到這個(gè)上下文對(duì)象臣镣。對(duì)象屬性引用鏈中只有上一層或者說(shuō)最后一層在調(diào)用中起作用。
function foo() {
console.log( this.a );
}
var obj2 = {
a: 42,
foo: foo
};
var obj1 = {
a: 2,
obj2: obj2
};
obj1.obj2.foo(); // 42
隱式綁定的函數(shù)在特定情況下會(huì)丟失綁定對(duì)象智亮,應(yīng)用到默認(rèn)綁定中
// 雖然bar是obj.foo的一個(gè)引用忆某,但是實(shí)際上,它引用的是foo函數(shù)本身阔蛉。
// bar()是一個(gè)不帶任何修飾的函數(shù)調(diào)用弃舒,應(yīng)用默認(rèn)綁定。
function foo() {
console.log( this.a );
}
var obj = {
a: 2,
foo: foo
};
var bar = obj.foo; // 函數(shù)別名
var a = "oops, global"; // a是全局對(duì)象的屬性
bar(); // "oops, global"
參數(shù)傳遞也是一種隱式賦值
3.顯式綁定
通過(guò)call(),apply()方法改變this的指向状原。
但是顯式綁定仍然無(wú)法解決綁定丟失的問(wèn)題聋呢。
解決綁定丟失的問(wèn)題:
- 硬綁定
創(chuàng)建了函數(shù) bar(),并在它的內(nèi)部手動(dòng)調(diào)用 foo.call(obj)颠区,因此強(qiáng)制把 foo 的 this 綁定到了 obj削锰。無(wú)論之后如何調(diào)用函數(shù) bar,它總會(huì)手動(dòng)在 obj 上調(diào)用 foo毕莱。這種綁定是一種顯式的強(qiáng)制綁定器贩,因此我們稱(chēng)之為硬綁定颅夺。 - api調(diào)用的上下文
JS許多內(nèi)置函數(shù)提供了一個(gè)可選參數(shù),被稱(chēng)之為“上下文”(context)蛹稍,其作用和bind(..)一樣吧黄,確保回調(diào)函數(shù)使用指定的this唆姐。這些函數(shù)實(shí)際上通過(guò)call(..)和apply(..)實(shí)現(xiàn)了顯式綁定稚字。
4.new綁定
使用new來(lái)調(diào)用函數(shù),或者說(shuō)發(fā)生構(gòu)造函數(shù)調(diào)用時(shí)厦酬,會(huì)自動(dòng)執(zhí)行下面的操作胆描。
- 1.創(chuàng)建(或者說(shuō)構(gòu)造)一個(gè)新對(duì)象。
- 2.這個(gè)新對(duì)象會(huì)被執(zhí)行[[Prototype]]連接仗阅。
- 3.這個(gè)新對(duì)象會(huì)綁定到函數(shù)調(diào)用的this昌讲。
- 4.如果函數(shù)沒(méi)有返回其他對(duì)象,那么new表達(dá)式中的函數(shù)調(diào)用會(huì)自動(dòng)返回這個(gè)新對(duì)象减噪。
使用new來(lái)調(diào)用foo(..)時(shí)短绸,會(huì)構(gòu)造一個(gè)新對(duì)象并把它(bar)綁定到foo(..)調(diào)用中的this。
function foo(a) {
this.a = a;
}
var bar = new foo(2);
console.log( bar.a ); // 2
手寫(xiě)一個(gè)new的實(shí)現(xiàn)
實(shí)現(xiàn)new的4步操作
function _new(fn, ...args) {
//先用Object創(chuàng)建一個(gè)空的對(duì)象,
const obj = {}
//將空對(duì)象掛到fn的原型鏈上
obj.__proto__ = fn.prototype
//改變this指向
const rel = fn.apply(obj, args)
//返回
return rel instanceof Object ? rel : obj
}
優(yōu)先級(jí)
new > 顯式綁定 > 隱式綁定 > 默認(rèn)綁定
備注
ES6 中的箭頭函數(shù)并不會(huì)使用四條標(biāo)準(zhǔn)的綁定規(guī)則筹裕,而是根據(jù)當(dāng)前的詞法作用域來(lái)決定this醋闭,具體來(lái)說(shuō),箭頭函數(shù)會(huì)繼承外層函數(shù)調(diào)用的 this 綁定(無(wú)論 this 綁定到什么)朝卒。這其實(shí)和 ES6 之前代碼中的 self = this 機(jī)制一樣证逻。