前言
我們這里將從最底層的顯隱式this綁定等類型去闡述记焊,讓你對this有一個清晰的認(rèn)識巾腕。采用逆向思維去講述。
一句話解釋this綁定
常見說法:誰調(diào)用它棚点,this就指向誰。
嚴(yán)格來說:this 的指向湾蔓,是在調(diào)用函數(shù)時根據(jù)執(zhí)行上下文所動態(tài)確定的瘫析。
啥意思呢,咱們來解釋下,看如下demo
function f1 () {
console.log(this)
}
function f2 () {
'use strict'
console.log(this)
}
f1() // window
f2() // undefined
此時我們調(diào)用的f1在window上
颁股,所以指向window
么库。嚴(yán)格模式就是undefined
(ps:react,vue等就是嚴(yán)格模式)甘有。
this優(yōu)先級
new(構(gòu)造函數(shù)) > call/apply/bind > 上下文對象中 > 普通函數(shù)
(這里公理诉儒,可以通過綁定不同情況this驗證。)
通過優(yōu)先級進行this的判斷
- 函數(shù)是否在new中調(diào)用(new綁定)?如果是的話this綁定的是新創(chuàng)建的對象亏掀。
var bar = new foo()
- 函數(shù)是否通過call忱反、apply(顯式綁定)或者硬綁定調(diào)用?如果是的話,this綁定的是 指定的對象滤愕。
var bar = foo.call(obj2)
- 函數(shù)是否在某個上下文對象中調(diào)用(隱式綁定)?如果是的話温算,this 綁定的是那個上 下文對象。
var bar = obj1.foo()
- 如果都不是的話间影,使用(默認(rèn)綁定)注竿。如果在嚴(yán)格模式下,就綁定到undefined魂贬,否則綁定到 全局對象巩割。
var bar = foo()
- 箭頭函數(shù) => 箭頭函數(shù)會繼承外層函數(shù)調(diào)用的 this 綁定,self = this 機制一樣付燥。
前面記住宣谈,面試夠用了,想了解更深入的請看逐一分析键科,說實話平時寫代碼闻丑,直接箭頭函數(shù)和bind就完美解決這些尷尬問題了,但是如果你想寫更底層的東西勋颖,做更高級嗦嗡,甚至專家級別,以下也是必備的饭玲。
逐一分析:
ps: 我們只在非嚴(yán)格模式討論酸钦,嚴(yán)格模式默認(rèn)綁定指向underfined。
1. 默認(rèn)綁定
function foo() {
console.log(this.a)
}
var a = 1;
foo(); // 1
分析:我們看 foo()
在最外層的window
上面咱枉,根據(jù)所以根據(jù)誰調(diào)用它卑硫,this就指向誰的原則
,所以window
上調(diào)用了foo
蚕断,window.a
就是我們在window
上設(shè)置的var a = 1
欢伏,則this.a = 1
;
2. 隱式綁定
2.1正常情況
function foo() {
console.log(this.a);
}
var obj = {
a: 1,
foo: foo
}
obj.foo(); // 1
分析:我們看 foo()
在obj
上面,根據(jù)所以根據(jù)誰調(diào)用它亿乳,this就指向誰的原則
硝拧,所以obj
上調(diào)用了foo
径筏,那么this
指向obj
,所以this.a
就是obj.a
;
2.2 隱式綁定丟失(主動賦值)
function foo() {
console.log(this.a);
}
var obj = {
a: 2,
foo: foo
};
var a = 1;
var bar = obj.foo; // 只是賦值 沒有調(diào)用執(zhí)行
bar(); // 1
分析:我們看 foo()
在obj
上面障陶,根據(jù)所以根據(jù)誰調(diào)用它滋恬,this就指向誰的原則
,所以obj
上調(diào)用了foo
抱究,那么this
指向obj
恢氯,所以this.a
就是obj.a
;
看似是丟失,其實原因在于調(diào)用的位置鼓寺,不信咱稍微改一下這句話var bar = obj.foo
:
function foo() {
console.log(this.a);
}
var obj = {
a: 2,
foo: foo
};
var a = 1;
var bar = obj.foo() ; // 2
我們此時就直接 var bar = obj.foo()
勋拟,咋答案就是2
了呢?
分析:我們對比下第一種方法是用var bar = obj.foo
妈候;吧obj.foo
賦值給bar
敢靡,然后我們調(diào)用了bar()
,調(diào)用的時候已經(jīng)在window
上了苦银。而第二種我們是直接obj.foo()
啸胧,其實還是在obj
上調(diào)用的(就是普通的隱式綁定)。所以原理就是在哪調(diào)用這個函數(shù)幔虏,this就指向誰
纺念。
2.3 隱式綁定丟失(函數(shù)傳參)
function foo() {
console.log('a', this.a);
}
var baz = {
a: 2,
foo: foo
};
var a = 1;
setTimeout(baz.foo, 100);
分析:一句話,函數(shù)傳參
就是隱式賦值
所计,跟主動賦值
是一個道理柠辞。
3. 顯式綁定(call团秽、apply主胧、bind)
3.1普通顯式綁定call、apply
function foo() {
console.log(this.a);
}
var obj = { a: 2 };
var obj2 = {a: 3}
foo.call(obj); // 2
foo.call(obj2); // 3
分析:call习勤,apply
的參數(shù)是誰踪栋,this
就指向誰。
此時你會發(fā)現(xiàn)之前說的誰調(diào)用它图毕,this就指向誰
夷都,就看不出來了,所以嚴(yán)格來說this 的指向予颤,是在調(diào)用函數(shù)時根據(jù)執(zhí)行上下文所動態(tài)確定的
囤官。
這里順帶說一下call,apply區(qū)別蛤虐,亮代碼:
function foo(b, c) {
console.log(this.a, b, c, arguments);
}
var obj = { a: 2 };
foo.call(obj, 3, 4); // 2,3,4
foo.apply(obj, [5, 6]); // 2,5,6
不同的:傳參不同党饮,call是單個傳參依次傳遞,apply是傳遞數(shù)組驳庭,傳遞的就是foo規(guī)定好的參數(shù)刑顺。
(我們也可以通過arguments獲取傳遞來的參數(shù)氯窍。)
3.2 硬綁定與Bind
大家看上面普通顯式綁定,每一次call不同的對象蹲堂,指向就發(fā)生變化狼讨,那么我們想讓this一直不變呢,那就使用硬綁定:
function foo() {
console.log(this.a);
}
var obj = { a: 2 };
var obj2 = { a: 3 };
var bar = function() {
foo.call(obj);
};
bar(); // 2
bar.call(obj); // 2
bar.call(obj2); // 3
分析:我們創(chuàng)建了函數(shù) bar()
柒竞,并在它的內(nèi)部手動調(diào)用了foo.call(obj)
政供,因此強制把foo 的 this
綁定到了 obj
。無論之后如何調(diào)用函數(shù) bar
能犯,它 總會手動在 obj
上調(diào)用foo
鲫骗。這種綁定是一種顯式的強制綁定,因此我們稱之為硬綁定踩晶。
Bind是es5內(nèi)置的硬綁定:
function foo() {
console.log(this.a);
}
var obj = { a: 2 };
var obj2 = { a: 3 };
var bar = foo.bind(obj);
bar(); // 2
bar.call(obj); // 2
bar.call(obj2); // 3
源碼分析执泰,請看這篇文章,說得很好:從一道面試題渡蜻,到“我可能看了假源碼”
3.3 硬綁定:softBind
這里不介紹了术吝,用的很少,有興趣同學(xué)可以研究下茸苇。
4. 構(gòu)造函數(shù)new 和 this
首先介紹一個常見面試題:new做了什么排苍?
- 創(chuàng)建一個新的對象;
- 將構(gòu)造函數(shù)的 this 指向這個新對象学密;
- 為這個對象添加屬性淘衙、方法等;
- 最終返回新對象腻暮。
代碼解釋:
var obj = {}
obj.__proto__ = Foo.prototype
Foo.call(obj)
如下是介紹this指向
- 普通版
function Foo() {
this.a = "a"
}
const f = new Foo()
console.log(f.a) // 'a'
- return 非對象版
function Foo() {
this.a = "a"
return 1
}
const f = new Foo()
console.log(f.a) // 'a'
- retrun 對象版
function Foo() {
this.a = "a"
return {a: "真假美猴王a"}
}
const f = new Foo()
console.log(f.a) // '真假美猴王a'
結(jié)論:如果構(gòu)造函數(shù)
中顯式返回的是一個對象
彤守,那么 this
就指向這個返回的對象
;如果返回的不是一個對象
哭靖,那么 this
仍然指向實例
具垫。
4. 箭頭函數(shù)中的 this 指向
- 普通 function 版
const foo = {
a: '1',
fn: function() {
setTimeout(function() {
console.log(this);
});
}
};
foo.fn(); // window
- 箭頭函數(shù) => 版
const foo = {
a: '1',
fn: function() {
setTimeout(() => {
console.log(this);
});
}
};
foo.fn(); // {a: "1", fn: ?}
結(jié)論:箭頭函數(shù)會繼承外層函數(shù)調(diào)用的 this 綁定
,和self = this 機制一樣试幽。
更多內(nèi)容可以看我的集錄: 全面攻陷js:更新中...