this 不指向函數(shù)本身粘优。
人們很容易把this 理解成指向函數(shù)自身菩暗,事實(shí)上肴敛,this 并不像我們所想的那樣指向函數(shù)本身璃哟。我們通過(guò)下面這段代碼來(lái)解釋為什么this 并是指向函數(shù)本身。
function foo(num) {
console.log( "foo: " + num );
// 記錄foo 被調(diào)用的次數(shù)
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
// foo 被調(diào)用了多少次产艾?
console.log( foo.count ); // 0
那么this.count++這個(gè)語(yǔ)句執(zhí)行結(jié)果去了哪呢疤剑,其實(shí),這里的this指向了全局對(duì)象闷堡,此時(shí)this.count值為NaN隘膘,因?yàn)樵谌謱?duì)象中,由于不存在count變量杠览,JavaScript會(huì)為我們自動(dòng)創(chuàng)建一個(gè)名為count的變量弯菊,其初始值為”undefied“,因此進(jìn)行++操作后踱阿,this.count值變成了NaN
this 不一定指向函數(shù)的作用域
第二種常見(jiàn)的誤解是管钳,this 指向函數(shù)的作用域。在某種情況下它是正確的软舌,但是在其他情況下它卻是錯(cuò)誤的才漆。
事實(shí)上,this 在任何情況下都不指向函數(shù)的詞法作用域佛点。在JavaScript 內(nèi)部醇滥,作用域確實(shí)和對(duì)象類似,可見(jiàn)的標(biāo)識(shí)符都是它的屬性。但是作用域“對(duì)象”無(wú)法通過(guò)JavaScript代碼訪問(wèn)腺办,它存在于JavaScript 引擎內(nèi)部焰手。
function foo() {
var a = 2;
this.bar();
}
function bar() {
console.log( this.a );
}
foo(); // ReferenceError: a is not defined
首先,這段代碼試圖通過(guò)this.bar() 來(lái)引用bar() 函數(shù)怀喉。調(diào)用bar() 最自然的方法是省略前面的this,直接使用詞法引用標(biāo)識(shí)符船响。
此外躬拢,編寫(xiě)這段代碼的開(kāi)發(fā)者還試圖使用this 聯(lián)通foo() 和bar() 的詞法作用域,從而讓bar() 可以訪問(wèn)foo() 作用域里的變量a见间。這是不可能實(shí)現(xiàn)的聊闯,你不能使用this 來(lái)引用一個(gè)詞法作用域內(nèi)部的東西。
每當(dāng)你想要把this 和詞法作用域的查找混合使用時(shí)米诉,一定要提醒自己菱蔬,這是無(wú)法實(shí)現(xiàn)的
this的綁定
this 的綁定和函數(shù)聲明的位置沒(méi)有任何關(guān)系,只取決于函數(shù)的調(diào)用方式史侣。當(dāng)一個(gè)函數(shù)被調(diào)用時(shí)拴泌,會(huì)創(chuàng)建一個(gè)活動(dòng)記錄(有時(shí)候也稱為執(zhí)行上下文)。這個(gè)記錄會(huì)包含函數(shù)在哪里被調(diào)用(調(diào)用棧)惊橱、函數(shù)的調(diào)用方法蚪腐、傳入的參數(shù)等信息。this 就是記錄的其中一個(gè)屬性税朴,會(huì)在函數(shù)執(zhí)行的過(guò)程中用到回季。
1、默認(rèn)綁定
獨(dú)立函數(shù)調(diào)用是最常用的函數(shù)調(diào)用類型正林∨菀唬可以把這條規(guī)則看作是無(wú)法應(yīng)用其他規(guī)則時(shí)的默認(rèn)規(guī)則。此時(shí)觅廓,this 默認(rèn)指向全局對(duì)象鼻忠。需要注意的是,在全局作用域中聲明的變量(var a = 2)就是全局對(duì)象的一個(gè)同名屬性哪亿,它們本質(zhì)上就是同一個(gè)東西粥烁。
function foo() {
console.log( this.a );
}
var a = 2;
foo(); // 2
如果使用嚴(yán)格模式(strict mode),那么全局對(duì)象將無(wú)法使用默認(rèn)綁定蝇棉,因此this 會(huì)綁定到undefined:
function foo() {
"use strict";
console.log( this.a );
}
var a = 2;
foo(); // TypeError: this is undefined
2讨阻、隱式綁定
隱式綁定考慮的是調(diào)用位置是否有上下文對(duì)象,或者說(shuō)是否被某個(gè)對(duì)象擁有或者包含篡殷,不過(guò)這種說(shuō)法可能會(huì)造成一些誤導(dǎo)钝吮。換句話說(shuō),就是某個(gè)函數(shù)被某個(gè)對(duì)象所擁有,則這個(gè)函數(shù)中的this一般情況下指向這個(gè)對(duì)象奇瘦。
function foo() {
console.log( this.a );
}
var obj = {
a: 2,
foo: foo
};
obj.foo(); // 2
需要注意的是:foo()的聲明方式棘催,無(wú)論是直接在obj 中定義還是先定義再添加為引用屬性,這個(gè)函數(shù)嚴(yán)格來(lái)說(shuō)都不屬于obj 對(duì)象耳标。然而醇坝,調(diào)用位置會(huì)使用obj 上下文來(lái)引用函數(shù),因此你可以說(shuō)函數(shù)被調(diào)用時(shí)obj 對(duì)象“擁有”或者“包含”它次坡。
需要注意的是呼猪,對(duì)象屬性引用鏈中只有最頂層或者說(shuō)最后一層會(huì)影響調(diào)用位置。比如:
function foo() {
console.log( this.a );
}
var obj2 = {
a: 42,
foo: foo
};
var obj1 = {
a: 2,
obj2: obj2
};
obj1.obj2.foo(); // 42
隱式丟失:下面代碼中砸琅,雖然bar 是obj.foo 的一個(gè)引用宋距,但是實(shí)際上,它引用的是foo 函數(shù)本身症脂,因此此時(shí)的bar() 其實(shí)是一個(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"
再看一個(gè)例子:
function foo() {
console.log( this.a );
}
function doFoo(fn) {
// fn 其實(shí)引用的是foo
fn(); // <-- 調(diào)用位置壶唤!
}
var obj = {
a: 2,
foo: foo
};
var a = "oops, global"; // a 是全局對(duì)象的屬性
doFoo( obj.foo ); // "oops, global"
參數(shù)傳遞其實(shí)就是一種隱式賦值,因此我們傳入函數(shù)時(shí)也會(huì)被隱式賦值兴蒸,所以結(jié)果和上一個(gè)例子一樣视粮。
3、顯式綁定
通過(guò)apply橙凳、call蕾殴、bind將this綁定到特定對(duì)象上,雖然bind可綁定到null上岛啸,但十分不推薦
4钓觉、new綁定
使用new 來(lái)調(diào)用函數(shù),或者說(shuō)發(fā)生構(gòu)造函數(shù)調(diào)用時(shí)坚踩,會(huì)自動(dòng)執(zhí)行下面的操作:
- 創(chuàng)建(或者說(shuō)構(gòu)造)一個(gè)全新的對(duì)象荡灾。
- 這個(gè)新對(duì)象會(huì)被執(zhí)行[[ 原型]] 連接。
- 這個(gè)新對(duì)象會(huì)綁定到函數(shù)調(diào)用的this瞬铸。
- 如果函數(shù)沒(méi)有返回其他對(duì)象批幌,那么new 表達(dá)式中的函數(shù)調(diào)用會(huì)自動(dòng)返回這個(gè)新對(duì)象。
function foo(a) {
this.a = a;
}
var bar = new foo(2);
console.log( bar.a ); // 2
使用new 來(lái)調(diào)用foo(..) 時(shí)嗓节,我們會(huì)構(gòu)造一個(gè)新對(duì)象并把它綁定到foo(..) 調(diào)用中的this上荧缘。new 是最后一種可以影響函數(shù)調(diào)用時(shí)this 綁定行為的方法,我們稱之為new 綁定拦宣。
優(yōu)先級(jí)
new綁定>顯式綁定>隱式綁定>默認(rèn)綁定
整理:《你不知道的JavaScript(上卷)》