ES5中令人頭暈的this

說到this梨与,就不得不提到function秉继,相信看過其它類似文章的同學(xué)也知道,正是由于調(diào)用function的對(duì)象不同盗棵,才導(dǎo)致了this的指向不同壮韭。所以以前老是去記憶每種調(diào)用function的情況所對(duì)應(yīng)的this,因?yàn)榍闆r有限而且很少纹因,所以這當(dāng)然是可行的——對(duì)于聰明人來說喷屋。所以我不得不思考另外一些方式來讓我記住。

那么首先我們需要明確的一個(gè)事情是:function也是對(duì)象

先來幾條綱領(lǐng):
1.函數(shù)執(zhí)行時(shí)瞭恰,首先看函數(shù)名前是否存在‘.’屯曹,存在-> ‘.’前面是誰,this就指向誰惊畏;不存在-> this指向window恶耽。
2.自執(zhí)行函數(shù)中的this指向window。
3.元素綁定事件后陕截,事件執(zhí)行時(shí)驳棱,回調(diào)函數(shù)中的this指向當(dāng)前事件元素。
4.構(gòu)造函數(shù)模式中(new Function)农曲,類中(函數(shù)體中)this.xxx = xxx社搅,this就是當(dāng)前類的實(shí)例驻债。
5.通過call、apply形葬、bind方法中第一個(gè)參數(shù)強(qiáng)行改變this指向合呐,call、apply笙以、bind參數(shù)中第一個(gè)參數(shù)就是方法調(diào)用者的this指向淌实。
當(dāng)call、apply猖腕、bind參數(shù)中第一個(gè)參數(shù)是空拆祈、null、undefined時(shí)倘感,this指向window

在原型模式中查找this方法步驟:
1)首先確定this的指向(上面5點(diǎn))
2)把this替換成對(duì)應(yīng)代碼塊
3)按照原型鏈機(jī)制放坏,一步步查找結(jié)果

同時(shí)我們還需要明確的一個(gè)事情是:
function執(zhí)行時(shí)是在某個(gè)特定的上下文中執(zhí)行的。那什么是上下文呢老玛?

  • 全局執(zhí)行環(huán)境
      全局環(huán)境是最外圍的一個(gè)執(zhí)行環(huán)境淤年。全局執(zhí)行環(huán)境被認(rèn)為是window對(duì)象。因此所有全局變量和函數(shù)都是作為window對(duì)象的屬性和方法創(chuàng)建的蜡豹。代碼載入瀏覽器時(shí)麸粮,全局執(zhí)行環(huán)境被創(chuàng)建(當(dāng)我們關(guān)閉網(wǎng)頁或者瀏覽器時(shí)全局執(zhí)行環(huán)境才被銷毀)。比如在一個(gè)頁面中镜廉,第一次載入JS代碼時(shí)創(chuàng)建一個(gè)全局執(zhí)行環(huán)境弄诲。
      這也是為什么閉包有一個(gè)內(nèi)存泄露的缺點(diǎn)。因?yàn)殚]包中外部函數(shù)被當(dāng)成了全局環(huán)境桨吊。所以不會(huì)被銷毀威根,一直保存在內(nèi)存中。

  • 函數(shù)執(zhí)行環(huán)境
    ?  每個(gè)函數(shù)都有自己的執(zhí)行環(huán)境视乐。當(dāng)執(zhí)行流進(jìn)入一個(gè)函數(shù)時(shí)洛搀,函數(shù)環(huán)境就會(huì)被推入一個(gè)環(huán)境棧中。當(dāng)函數(shù)執(zhí)行完之后佑淀,棧將其環(huán)境彈出留美,把控制權(quán)返回給之前的執(zhí)行環(huán)境。函數(shù)執(zhí)行環(huán)境的變量對(duì)象是該函數(shù)的活動(dòng)對(duì)象(activation object)伸刃。
      這就是上下文谎砾,函數(shù)執(zhí)行時(shí)它也需要一些額外的信息來支撐它的運(yùn)行。那么既然function是對(duì)象的話捧颅,就會(huì)有方法景图。而function中最核心的方法是call方法。因此我們就從這兒入手碉哑。

  • call方法
    先來看一下如何使用call方法:

function say(content) {  
       console.log("From " + this + ": Hello "+ content);  
   }  
say.call("Bob", "World"); //==> From Bob: Hello World 

·Step1: 把第二個(gè)到最后一個(gè)參數(shù)作為函數(shù)執(zhí)行時(shí)要傳入的參數(shù)
·Step2: 把函數(shù)執(zhí)行時(shí)的this指向第一個(gè)參數(shù)
·Step3: 在上面這個(gè)特殊的上下文中執(zhí)行函數(shù)
上面例子中挚币,我們通過call方法亮蒋,讓say函數(shù)執(zhí)行時(shí)的this指向"Bob",然后把"World"作為參數(shù)傳進(jìn)去妆毕,所以輸出結(jié)果是可以預(yù)見的慎玖。
js執(zhí)行函數(shù)時(shí)會(huì)默認(rèn)完成以上的步驟,你可以把直接調(diào)用函數(shù)理解為一種語法糖
比如

function say(word) {  
   console.log(world);  
}  
say("Hello world");  
  
say.call(window, "Hello world");  

以上可以把say("Hello world")看做是say.call(window,"Hello world")的語法糖笛粘。
這個(gè)結(jié)論非常關(guān)鍵
所以以后每次看見functionName(xxx)的時(shí)候趁怔,你需要馬上在腦海中把它替換為functionName.call(window,xxxx),這對(duì)你理解this的指向非常重要薪前。不過也有例外润努,在ES5的strict mode中call的第一個(gè)參數(shù)不是window而是undefined。之后的例子我假設(shè)總是不在strictmode下序六,但你需要記住strictmode有一點(diǎn)兒不同任连。
對(duì)于匿名函數(shù)來說,上面的結(jié)論也是成立的

(function(name) {  
    //  
})("aa");  
//等價(jià)于  
(function(name) {  
    //  
}).call(window, "aa"); 

函數(shù)作為對(duì)象的方法被調(diào)用
直接來看代碼:

var person = {  
    name : "caibirdme",  
    run : function(time) {  
        console.log(this.name + "has been running for over "+ time+ " minutes");  
    }  
};  
person.run(30); //==> caibirdme has been running for over 30 minutes  
//等價(jià)于  
person.run.call(person, 30); // the same  

你會(huì)發(fā)現(xiàn)這里call的第一個(gè)參數(shù)是person而不是window例诀。
當(dāng)你明白了這兩點(diǎn),下意識(shí)地把函數(shù)調(diào)用翻譯成foo.call()的形式裁着,明確call的第一個(gè)參數(shù)繁涂,那基本上this的問題就難不住你了。
還是來舉幾個(gè)例子吧
例一:

function hello(thing) {    
  console.log(this + " says hello " + thing);  
}  
  
person = { name: "caibirdme" }    
person.hello = hello;  
  
person.hello("world") // 相當(dāng)于執(zhí)行 person.hello.call(person, "world")  
//caibirdme says hello world  
  
hello("world") // 相當(dāng)于執(zhí)行 hello.call(window, "world")   
//[object DOMWindow]world  

例二:

var obj = {  
    x: 20,  
    f: function(){ console.log(this.x); }  
};  
  
obj.f(); // obj.f.call(obj)  
//==> 20  
  
obj.innerobj = {  
    x: 30,  
    f: function(){ console.log(this.x); }  
}  
  
obj.innerobj.f(); // obj.innerobj.f.call(obj.innerobj)  
// ==> 30  

例三:

var x = 10;  
var obj = {  
    x: 20,  
    f: function(){  
        console.log(this.x); //this equals obj  
                // ==> 20  
        var foo = function(){ console.log(this.x); }  
        foo(); // foo.call(window)  
                //foo中this被指定為window二驰,所以==> 10  
    }  
};  
  
obj.f();  // obj.f.call(obj)  
// ==> 20 10  

由例三引出一個(gè)非常common的問題扔罪,如果我想讓foo輸出20怎么辦?這時(shí)候需要用到一點(diǎn)小技巧
例四:

var x = 10;  
var obj = {  
    x: 20,  
    f: function(){  
        console.log(this.x);  
        var that = this; //使用that保留當(dāng)前函數(shù)執(zhí)行上下文的this  
        var foo = function(){ console.log(that.x); } //此時(shí)foo函數(shù)中的this仍然指向window桶雀,但我們使用that取得obj  
        foo(); // foo.call(window)  
    }  
};  
  
obj.f(); obj.f.call(obj)  
// ==> 20 20  

再來一個(gè)稍微難一點(diǎn)點(diǎn)的(但其實(shí)用call替換法一點(diǎn)兒也不難)
例五:

var x = 10;  
var obj = {  
    x: 20,  
    f: function(){ console.log(this.x); }  
};  
  
obj.f(); // obj.f.call(obj)  
// ==> 20  
  
var fOut = obj.f;  
fOut(); // fOut.call(window)  
//==> 10  
  
var obj2 = {  
    x: 30,  
    f: obj.f  
}  
  
obj2.f(); // obj2.f.call(obj2)  
//==> 30  

例五如果沒有明確:
es5中this是在執(zhí)行是才會(huì)被確認(rèn)的
是會(huì)出錯(cuò)的矿酵。
可能會(huì)認(rèn)為說 obj.f 那個(gè)函數(shù)定義在obj里面,那 this 就該指向obj矗积。
用于構(gòu)造函數(shù)
先看一段代碼:

func person(name) {  
    this.name = name;  
}  
var caibirdme = new person("deen");  
// caibirdme.name == deen  

函數(shù)在用作構(gòu)造函數(shù)時(shí)同樣可以用call方法去代替全肮,那這里怎么代替呢?
這里又需要明確一點(diǎn):
new constrcut()是一種創(chuàng)建對(duì)象的語法糖
它等價(jià)于

function person(name) {  
   this.name = name;  
}  
var foo = new person("deen");  
//通過new創(chuàng)建了一個(gè)對(duì)象  
//new是一種語法糖棘捣,new person等價(jià)于  
var bar = (function(name) {  
    var _newObj = {  
        constructor : person,  
        __proto__ : person.prototype,  
    };  
    _newObj.constructor(name); // _newObj.constructor.call(_newObj, name)  
    return _newObj;  
})();  

new的時(shí)候this就指向新的對(duì)象
總結(jié)來說就是下面兩個(gè)等價(jià)變形:
· foo() ---> foo.call(window)
· obj.foo() --> obj.foo.call(obj)
只要理解以上兩個(gè)變形辜腺,this就不再是問題啦!乍恐!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市茵烈,隨后出現(xiàn)的幾起案子百匆,更是在濱河造成了極大的恐慌,老刑警劉巖呜投,帶你破解...
    沈念sama閱讀 221,695評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件加匈,死亡現(xiàn)場(chǎng)離奇詭異寄症,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)矩动,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門有巧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人悲没,你說我怎么就攤上這事篮迎。” “怎么了示姿?”我有些...
    開封第一講書人閱讀 168,130評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵甜橱,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我栈戳,道長(zhǎng)岂傲,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,648評(píng)論 1 297
  • 正文 為了忘掉前任子檀,我火速辦了婚禮镊掖,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘褂痰。我一直安慰自己亩进,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評(píng)論 6 397
  • 文/花漫 我一把揭開白布缩歪。 她就那樣靜靜地躺著归薛,像睡著了一般。 火紅的嫁衣襯著肌膚如雪匪蝙。 梳的紋絲不亂的頭發(fā)上主籍,一...
    開封第一講書人閱讀 52,268評(píng)論 1 309
  • 那天,我揣著相機(jī)與錄音逛球,去河邊找鬼千元。 笑死,一個(gè)胖子當(dāng)著我的面吹牛需忿,可吹牛的內(nèi)容都是我干的诅炉。 我是一名探鬼主播,決...
    沈念sama閱讀 40,835評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼屋厘,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼涕烧!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起汗洒,我...
    開封第一講書人閱讀 39,740評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤议纯,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后溢谤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瞻凤,經(jīng)...
    沈念sama閱讀 46,286評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡憨攒,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了阀参。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片肝集。...
    茶點(diǎn)故事閱讀 40,505評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖蛛壳,靈堂內(nèi)的尸體忽然破棺而出杏瞻,到底是詐尸還是另有隱情,我是刑警寧澤衙荐,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布捞挥,位于F島的核電站,受9級(jí)特大地震影響忧吟,放射性物質(zhì)發(fā)生泄漏砌函。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評(píng)論 3 333
  • 文/蒙蒙 一溜族、第九天 我趴在偏房一處隱蔽的房頂上張望讹俊。 院中可真熱鬧,春花似錦斩祭、人聲如沸劣像。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至绑青,卻和暖如春诬像,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背闸婴。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工坏挠, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人邪乍。 一個(gè)月前我還...
    沈念sama閱讀 48,921評(píng)論 3 376
  • 正文 我出身青樓降狠,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親庇楞。 傳聞我的和親對(duì)象是個(gè)殘疾皇子榜配,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評(píng)論 2 359