回家的路上膳灶,我試圖忘掉瑪利亞·塞克斯咱士,當我爸跟我說晚安的時候--他每次喝完一瓶啤酒就會醉過去,可今天他喝了兩瓶(驚嘆!)--我試著忘記瑪利亞·塞克斯序厉。半小時以后锐膜,伊奇來到我的房里,剛洗完淋浴脂矫,散發(fā)著清新的味道枣耀,穿著破爛的“探險家朵拉”睡衣,給我臉頰上來了一個邋遢的濕吻庭再,我試著忘記她捞奕;一個小時之后,我媽來到房間門外拄轻,說:“我真為你驕傲颅围,薩姆『薮辏”這時院促,我還在想著她。 --《忽然七日》
每個函數內部都有兩個特殊的對象斧抱,分別是arguments對象和this對象常拓。對于arguments對象,我們知道他是一個類數組辉浦,里面的項是傳遞進來的參數弄抬;還需要注意的地方是它還具有一個callee屬性,arguments.callee屬性的值就是函數本身宪郊。同時每個函數都包含有下面三個屬性:length掂恕,prototype,caller弛槐。length屬性表明函數所期望接收的參數個數懊亡;prototype屬性就不解釋了;caller屬性的值是調用該函數的那個函數的一個引用乎串。
而我們今天要談的就是函數內部的一個特殊的對象——this店枣。this引用的是函數據以執(zhí)行的環(huán)境對象,對于非箭頭函數來說:具體指向哪個對象取決于函數調用時所處的環(huán)境叹誉,而不是函數被聲明時的那個環(huán)境艰争;但是對于的箭頭函數的情況便是和此相反:具體指向哪個對象取決于函數被聲明時所處的那個環(huán)境,而不是被調用時所處的環(huán)境桂对。
分析普通函數的調用情況甩卓,大致可以分為下面幾種:
- 1.作為對象的方法調用;
- 2.作為普通函數被調用蕉斜;
- 3.作為構造器函數被調用逾柿;
- 4.被某些能夠延長作用域鏈的函數間接調用缀棍,比如Function.prototype.call和Function.prototype.bind函數。
這里既然提到了Function.prototype.apply()机错,Function.prototype.call()的話爬范,那就打個岔吧,在加入Function.prototype.bind()弱匪,下面捋一捋他們的區(qū)別青瀑。
首先,三個函數都能夠改變函數所處的環(huán)境對象萧诫。其中apply()和call()是改變后立馬執(zhí)行的斥难,這兩者之間的區(qū)別是apply()能夠接收數組以及類數組對象作為第二個參數以便傳入多個參數;而call()就不行帘饶,他必須顯式的把每一個參數分別作為call的第二參數哑诊,第三參數依次傳遞過去。而對于bind函數來說及刻,這個方法會創(chuàng)建一個函數的實例镀裤,他的this值會被綁定到那個傳入的參數上。和前兩者的關鍵區(qū)別在于缴饭,他不執(zhí)行暑劝。需要注意的地方是:如果我們對上面三個函數傳入的環(huán)境對象是null或者undefined的話,那么函數的實際執(zhí)行環(huán)境將會是全局執(zhí)行環(huán)境颗搂,在瀏覽器中就是window铃岔,在node環(huán)境里就是global。下面可以舉個例子看一下:
var a = 999;
function fun(){console.log(this.a);}
fun.apply(null);//9
言歸正傳峭火,下面來討論一下在不同的調用函數的方式下this指向哪個對象的分析方法:
- 1.當作為對象方法被調用時,this指向該對象智嚷;
- 2.當作為普通函數調用時卖丸,this總是指向全局對象。這一點是很讓人迷惑的盏道,看看下面的這個例子:
var a = 999;
var obj = {"a" : 111, "fun": function(){
console.log(this.a);
var test = function(){console.log(this.a);
test();
}};
obj.fun();//111/n999
可以很明顯的看到稍浆,我們的fun()被調用是返回的是999,說明他的執(zhí)行環(huán)境是全局環(huán)境猜嘱。對于這種情況衅枫,有的時候的確是會給我們帶來挺多不必要的麻煩,比如說下面這個例子:
window.id = "window";
var callback = function(){
console.log(this.id);
};
document.getElementById("div1").onclick = function(){
console.log(this.id);//"div1"
callback();//"window"
}
對于這個callback而言朗伶,我們最初是希望它的環(huán)境對象是div1的DOM對象的弦撩,然而實際上卻被綁定到了全局對象window上,而這并不是我們所希望的论皆。當然益楼,這樣做也是具有解決辦法的猾漫,像下面這樣。
window.id = "window";
document.getElementById("div1").onclick = function(){
var that = this;
console.log(this.id);//"div1"
var callback = function(){console.log(that.id);};
callback();//"div1"
}
或者通過call和apply后者bind來改變環(huán)境對象感凤。
window.id = "window";
var callback = function(){
console.log(this.id);
};
document.getElementById("div1").onclick = function(){
console.log(this.id);//"div1"
callback.apply(this);//"div1"
}
- 3.作為構造器函數調用:凡是正常的構造器函數悯周,他們之中的this都是引用著構造器所返回的對象的。
- 4.當函數利用apply()或者call()以及bind()改變環(huán)境對象時陪竿,函數的環(huán)境對象為這三個方法所傳入的那第一個參數對象禽翼。
拓展:思考下面這樣這樣會不會出錯
var getId = document.getElementById;
getId("someID");
答案是會出錯的。首先族跛,需要明確的是:函數的名字僅僅是一個包含指針的變量而已闰挡。因此,即使在不同的環(huán)境中執(zhí)行庸蔼,他們所指向的都是同一個函數解总。因此,我們的document.getElementById和getId都是指向同一個函數的姐仅,不同的時他們所指向的環(huán)境對象的不同花枫,對于前者來說他的環(huán)境對象時document而后者的環(huán)境對象確實window。而對于該函數的內部實現機理來說掏膏,我們時必須要求為document的劳翰,所以調用getID的話會出錯。
我們活著的每一刻背后都隱藏著成千上萬個不一樣的瞬間馒疹。