作為一個(gè)jser怜械,編碼生涯中最經(jīng)常碰到的一個(gè)問題就是this
指向問題,在看了N多資料后元莫,大家得出了一個(gè)結(jié)論:this
總是指向函數(shù)的調(diào)用者氮昧。當(dāng)時(shí)看到這個(gè)答案的時(shí)候框杜,我的想法是“我擦,終于有一個(gè)標(biāo)準(zhǔn)了”袖肥,但是在編碼實(shí)踐中咪辱,卻發(fā)現(xiàn)這句標(biāo)準(zhǔn)很難應(yīng)用起來,甚至不是準(zhǔn)確的椎组。
最近在看了《你不知道的js》(You-Dont-Know-JS)之后油狂,對(duì)this
有了更加準(zhǔn)確和完整的認(rèn)識(shí),現(xiàn)總結(jié)出來分享給大家。
結(jié)論:掌握this
的兩個(gè)方法
1 心法:this的指向是在運(yùn)行時(shí)決定的
2 操作方法:函數(shù)調(diào)用時(shí)专筷,前面是否有對(duì)象修飾弱贼,有,則一般this
指向該對(duì)象磷蛹,無吮旅,則一般指向window(在嚴(yán)格模式下,為undefined)
- 方法二的一般情況味咳,指的是函數(shù)內(nèi)部沒有apply庇勃、call、bind槽驶、new责嚷、es6的箭頭函數(shù)等,一般情況已經(jīng)足以幫我們分辨
this
了掂铐,對(duì)于其他情況罕拂,由于有明確的指定this
的操作,我們反而不容易被迷惑
接下來詳細(xì)講解兩個(gè)方法
1 this的指向是在運(yùn)行時(shí)決定的
對(duì)于this
,通常有兩個(gè)錯(cuò)誤認(rèn)識(shí)
1.1 錯(cuò)誤認(rèn)識(shí):this
指向function 本身
function foo(num) {
console.log( "foo: " + num );
// keep track of how many times `foo` is called
this.count++;
}
foo.count = 0;
var i;
for (i=0; i<10; i++) {
if (i > 5) {
foo( i );
}
}
// foo: 6
// foo: 7
// foo: 8
// foo: 9
// how many times was `foo` called?
console.log( foo.count ); // 0 -- WTF?
可以看到foo.count并沒有自增全陨,說明this并不指向函數(shù)本身
1.2 錯(cuò)誤認(rèn)識(shí):this
指向function 的作用域
function foo() {
var a = 2;
this.bar();
}
function bar() {
console.log( this.a );
}
foo(); //undefined
寫出這段代碼的同學(xué)聂受,可能是想通過this
來創(chuàng)建foo()
和bar()
作用域之間的橋梁,這樣bar()
就可以訪問foo()
中的變量a
了烤镐,不過這樣連接的橋梁不可能的。
1.3 小結(jié)
this
不是編碼階段綁定的棍鳖,而是在運(yùn)行時(shí)綁定炮叶,指向的是由函數(shù)調(diào)用來決定的上下文環(huán)境
2 this
指向規(guī)則
2.1 默認(rèn)綁定
function foo() {
console.log( this.a );
}
var a = 2;
foo(); // 2
獨(dú)立函數(shù)調(diào)用時(shí),如果沒有其他規(guī)則渡处,則啟動(dòng)默認(rèn)規(guī)則镜悉,this
指向全局對(duì)象,在瀏覽器環(huán)境中就是window對(duì)象医瘫,如果在嚴(yán)格模式侣肄,this
指向undefined
.
2.2 隱式綁定
function foo() {
console.log( this.a );
}
var obj = {
a: 2,
foo: foo
};
obj.foo(); // 2
隱式綁定有兩點(diǎn)需要注意
2.2.1 只有最后一層對(duì)象屬性的引用鏈才決定函數(shù)調(diào)用中的this
function foo() {
console.log( this.a );
}
var obj2 = {
a: 42,
foo: foo
};
var obj1 = {
a: 2,
obj2: obj2
};
obj1.obj2.foo(); // 42
2.2.2 隱式綁定丟失
function foo() {
console.log( this.a );
}
var obj = {
a: 2,
foo: foo
};
var bar = obj.foo; // function reference/alias!
var a = "oops, global"; // `a` also property on global object
bar(); // "oops, global"
這點(diǎn)尤其需要注意,var bar = obj.foo;
一行賦值語句就會(huì)造成this
的丟失。
我們?nèi)粘懙幕卣{(diào)函數(shù)和setTimeout函數(shù)醇份,經(jīng)過形參傳遞稼锅、賦值,都會(huì)造成引用丟失,這里一定要注意
function foo() {
console.log( this.a );
}
var obj = {
a: 2,
foo: foo
};
var a = "oops, global"; // `a` also property on global object
setTimeout( obj.foo, 100 ); // "oops, global"
function setTimeout(fn,delay) {
// wait (somehow) for `delay` milliseconds
fn(); // <-- call-site!
}
2.3 顯式綁定
function foo() {
console.log( this.a );
}
var obj = {
a: 2
};
foo.call( obj ); // 2
顯式綁定指的就是僚纷,通過call 或apply矩距,以及bind 方法來制定this
2.4 通過new 綁定
function foo(a) {
this.a = a;
}
var bar = new foo( 2 );
console.log( bar.a ); // 2
我們知道通過new 調(diào)用function時(shí),實(shí)際上編譯器在內(nèi)部先創(chuàng)建了一個(gè)object怖竭,然后把this
指向該object锥债,最后在隱含的return出來,所以在執(zhí)行期間,this
就是指向該新創(chuàng)建的對(duì)象
3 規(guī)則生效優(yōu)先級(jí)
下面通過偽代碼哮肚,來說明綁定規(guī)則
switch(rule){
case ‘函數(shù)通過new來調(diào)用’:
var bar = new foo()
this => 新創(chuàng)建的對(duì)象登夫;
break;
case ‘函數(shù)通過call apply bind等來調(diào)用’:
var bar = foo.call( obj2 )
this => 該明確指向的對(duì)象允趟;
break恼策;
case ‘函數(shù)通過上下文對(duì)象屬性方法來調(diào)用’:
obj1.foo()
this => 該上下文對(duì)象
break;
default ‘默認(rèn)綁定’:
var bar = foo()
非嚴(yán)格模式:this => 全局對(duì)象拼窥;
嚴(yán)格模式: this=> undefined;
break戏蔑;
}
4 綁定的異常情況
4.1 忽略this
如果把null
或undefined
,作為this
的參數(shù)傳遞給call
,apply
或bind
,那這個(gè)值將會(huì)被忽略鲁纠,而啟用默認(rèn)綁定規(guī)則
function foo() {
console.log( this.a );
}
var a = 2;
foo.call( null ); // 2
4.2 ES6 箭頭函數(shù)
箭頭函數(shù)不同于上述4條規(guī)則总棵,把this
綁定在當(dāng)前函數(shù)作用域中,且不能被其他調(diào)用方式改變改含!
function foo() {
// return an arrow function
return (a) => {
// `this` here is lexically adopted from `foo()`
console.log( this.a );
};
}
var obj1 = {
a: 2
};
var obj2 = {
a: 3
};
var bar = foo.call( obj1 );
bar.call( obj2 ); // 2, not 3!
總結(jié)
兩個(gè)方法
1 心法:this的指向是在運(yùn)行時(shí)決定的
2 操作方法:函數(shù)調(diào)用時(shí)情龄,前面是否有對(duì)象修飾,有捍壤,則一般this
指向該對(duì)象骤视,無,則一般指向window(在嚴(yán)格模式下鹃觉,為undefined)
四種規(guī)則
new > call apply bind > 上下文 > 默認(rèn)
特殊情況
箭頭函數(shù)綁定到函數(shù)作用域
上下文隱式綁定专酗,注意this丟失