本文地址:http://www.cnblogs.com/dongcanliang/p/7054176.html
前言
this 指向問題是入坑前端必須了解知識(shí)點(diǎn)拆讯,現(xiàn)在迎來了ES6時(shí)代养葵,因?yàn)榧^函數(shù)的出現(xiàn)撮竿,所以感覺有必要對(duì) this 問題梳理一下尘喝,遂有此文
在非箭頭函數(shù)下啸蜜, this 指向調(diào)用其所在函數(shù)的對(duì)象杯聚,而且是離誰近就是指向誰(此對(duì)于常規(guī)對(duì)象烛芬,原型鏈辆童, getter & setter等都適用)宜咒;構(gòu)造函數(shù)下,this與被創(chuàng)建的新對(duì)象綁定把鉴;DOM事件故黑,this指向觸發(fā)事件的元素;內(nèi)聯(lián)事件分兩種情況庭砍,bind綁定场晶, call & apply 方法等, 容以下一步一步討論怠缸。箭頭函數(shù)也會(huì)穿插其中進(jìn)行討論诗轻。
全局環(huán)境下
在全局環(huán)境下,this 始終指向全局對(duì)象(window), 無論是否嚴(yán)格模式揭北;
console.log(this.document ===document);// true// 在瀏覽器中扳炬,全局對(duì)象為 window 對(duì)象:console.log(this===window);// truethis.a =37;console.log(window.a);// 37
函數(shù)上下文調(diào)用
函數(shù)直接調(diào)用
普通函數(shù)內(nèi)部的this分兩種情況,嚴(yán)格模式和非嚴(yán)格模式搔体。
非嚴(yán)格模式下恨樟,this 默認(rèn)指向全局對(duì)象window
functionf1(){returnthis;}f1() ===window;// true
而嚴(yán)格模式下, this為undefined
functionf2(){? "use strict";// 這里是嚴(yán)格模式returnthis;}f2() ===undefined;// true
對(duì)象中的this
對(duì)象內(nèi)部方法的this指向調(diào)用這些方法的對(duì)象疚俱,
函數(shù)的定義位置不影響其this指向劝术,this指向只和調(diào)用函數(shù)的對(duì)象有關(guān)。
多層嵌套的對(duì)象呆奕,內(nèi)部方法的this指向離被調(diào)用函數(shù)最近的對(duì)象(window也是對(duì)象养晋,其內(nèi)部對(duì)象調(diào)用方法的this指向內(nèi)部對(duì)象, 而非window)登馒。
//1varo = {prop:37,f:function(){returnthis.prop;? }};console.log(o.f());//37vara = o.f;console.log(a())://undefinedvaro = {prop:37};functionindependent(){returnthis.prop;}o.f = independent;console.log(o.f());// logs 37//2o.b = {g: independent,prop:42};console.log(o.b.g());// logs 42
原型鏈中this
原型鏈中的方法的this仍然指向調(diào)用它的對(duì)象匙握,與以上討論一致;看個(gè)例子陈轿,
varo = {f:function(){returnthis.a +this.b;? }};varp =Object.create(o);p.a =1;p.b =4;console.log(p.f());// 5
可以看出圈纺, 在p中沒有屬性f秦忿,當(dāng)執(zhí)行p.f()時(shí),會(huì)查找p的原型鏈蛾娶,找到 f 函數(shù)并執(zhí)行灯谣,但這與函數(shù)內(nèi)部this指向?qū)ο?p 沒有任何關(guān)系,只需記住誰調(diào)用指向誰蛔琅。
以上對(duì)于函數(shù)作為getter & setter 調(diào)用時(shí)同樣適用胎许。
構(gòu)造函數(shù)中this
構(gòu)造函數(shù)中的this與被創(chuàng)建的新對(duì)象綁定。
注意:當(dāng)構(gòu)造器返回的默認(rèn)值是一個(gè)this引用的對(duì)象時(shí)罗售,可以手動(dòng)設(shè)置返回其他的對(duì)象辜窑,如果返回值不是一個(gè)對(duì)象,返回this寨躁。
functionC(){this.a =37;}varo =newC();console.log(o.a);// logs 37functionC2(){this.a =37;return{a:38};}varb =newC2();console.log(b.a);// logs 38
以上兩個(gè)例子內(nèi)部的this都指向?qū)ο髈, 看到這里的你不妨在控制臺(tái)執(zhí)行下以上代碼穆碎,看看對(duì)象 o 和 b ,這些是屬于構(gòu)造函數(shù)的內(nèi)容了职恳,此處不多做介紹所禀。(C2函數(shù)中的this.a = 37 對(duì)整個(gè)過程完全沒有影響的, 可以被忽略的)放钦。
call & apply
當(dāng)函數(shù)通過Function對(duì)象的原型中繼承的方法 call() 和 apply() 方法調(diào)用時(shí)色徘, 其函數(shù)內(nèi)部的this值可綁定到 call() & apply() 方法指定的第一個(gè)對(duì)象上, 如果第一個(gè)參數(shù)不是對(duì)象操禀,JavaScript內(nèi)部會(huì)嘗試將其轉(zhuǎn)換成對(duì)象然后指向它褂策。
例子:
functionadd(c, d){returnthis.a + this.b + c + d;}var o = {a:1, b:3};add.call(o,5,7); //1+3+5+7=16add.apply(o, [10,20]); //1+3+10+20=34functiontt(){? console.log(this);}// 返回對(duì)象見下圖(圖1)tt.call(5);? // Number {[[PrimitiveValue]]:5} tt.call('asd'); // String {0:"a",1:"s",2:"d", length:3,[[PrimitiveValue]]:"asd"}
bind 方法
bind方法在ES5引入, 在Function的原型鏈上床蜘,?Function.prototype.bind辙培。通過bind方法綁定后, 函數(shù)將被永遠(yuǎn)綁定在其第一個(gè)參數(shù)對(duì)象上邢锯, 而無論其在什么情況下被調(diào)用扬蕊。
functionf(){returnthis.a;}varg = f.bind({a:"azerty"});console.log(g());// azertyvaro = {a:37,f:f,g:g};console.log(o.f(), o.g());// 37, azerty
DOM 事件處理函數(shù)中的 this & 內(nèi)聯(lián)事件中的 this
DOM事件處理函數(shù)
當(dāng)函數(shù)被當(dāng)做監(jiān)聽事件處理函數(shù)時(shí), 其 this 指向觸發(fā)該事件的元素 (針對(duì)于addEventListener事件)
// 被調(diào)用時(shí)丹擎,將關(guān)聯(lián)的元素變成藍(lán)色functionbluify(e){//在控制臺(tái)打印出所點(diǎn)擊元素console.log(this);//阻止時(shí)間冒泡e.stopPropagation();//阻止元素的默認(rèn)事件e.preventDefault();this.style.backgroundColor ='#A5D9F3';? ? }// 獲取文檔中的所有元素的列表varelements =document.getElementsByTagName('*');// 將bluify作為元素的點(diǎn)擊監(jiān)聽函數(shù)尾抑,當(dāng)元素被點(diǎn)擊時(shí),就會(huì)變成藍(lán)色for(vari=0; i
以上代碼建議在網(wǎng)頁中執(zhí)行以下蒂培,看下效果再愈。
內(nèi)聯(lián)事件
內(nèi)聯(lián)事件中的this指向分兩種情況:
當(dāng)代碼被內(nèi)聯(lián)處理函數(shù)調(diào)用時(shí),它的this指向監(jiān)聽器所在的DOM元素
當(dāng)代碼被包括在函數(shù)內(nèi)部執(zhí)行時(shí)护戳,其this指向等同于 ****函數(shù)直接調(diào)用****的情況翎冲,即在非嚴(yán)格模式指向全局對(duì)象window, 在嚴(yán)格模式指向undefined
頁面的代碼塊
在瀏覽器內(nèi)顯示三個(gè)按鈕
依次點(diǎn)擊上邊的三個(gè)按鈕后在控制臺(tái)的輸出結(jié)果媳荒,
建議自己操作一遍以便于更好的理解抗悍。
setTimeout & setInterval
對(duì)于延時(shí)函數(shù)內(nèi)部的回調(diào)函數(shù)的this指向全局對(duì)象window(當(dāng)然我們可以通過bind方法改變其內(nèi)部函數(shù)的this指向)
看下邊代碼及截圖
//默認(rèn)情況下代碼functionPerson(){this.age =0;? ? ? setTimeout(function(){console.log(this);? ? },3000);}varp =newPerson();//3秒后返回 window 對(duì)象==============================================//通過bind綁定functionPerson(){this.age =0;? ? ? setTimeout((function(){console.log(this);? ? }).bind(this),3000);}varp =newPerson();//3秒后返回構(gòu)造函數(shù)新生成的對(duì)象 Person{...}
箭頭函數(shù)中的 this
由于箭頭函數(shù)不綁定this驹饺, 它會(huì)捕獲其所在(即定義的位置)上下文的this值, 作為自己的this值缴渊,
所以 call() / apply() / bind() 方法對(duì)于箭頭函數(shù)來說只是傳入?yún)?shù)赏壹,對(duì)它的 this 毫無影響。
考慮到 this 是詞法層面上的衔沼,嚴(yán)格模式中與 this 相關(guān)的規(guī)則都將被忽略蝌借。(可以忽略是否在嚴(yán)格模式下的影響)
因?yàn)榧^函數(shù)可以捕獲其所在上下文的this值 所以
functionPerson(){this.age =0;? ? ? setInterval(()=>{// 回調(diào)里面的 `this` 變量就指向了期望的那個(gè)對(duì)象了this.age++;? ? },3000);}varp =newPerson();
以上代碼可以得到我們所以希望的值,下圖可以看到指蚁,在setTimeout中的this指向了構(gòu)造函數(shù)新生成的對(duì)象菩佑,而普通函數(shù)指向了全局window對(duì)象
varadder = {base:1,add:function(a){varf =v=>v +this.base;returnf(a);? },addThruCall:functioninFun(a){varf =v=>v +this.base;varb = {base:2};returnf.call(b, a);? }};console.log(adder.add(1));// 輸出 2console.log(adder.addThruCall(1));// 仍然輸出 2(而不是3,其內(nèi)部的this并沒有因?yàn)閏all() 而改變欣舵,其this值仍然為函數(shù)inFun的this值擎鸠,指向?qū)ο骯dder
bind() & apply() 讀者可以自行測(cè)試
對(duì)于是否嚴(yán)格模式示例代碼(可以復(fù)制進(jìn)控制臺(tái)進(jìn)行驗(yàn)證)
varf =()=>{'use strict';returnthis};varp =()=>{returnthis};console.log(1,f() ===window);console.log(2,f() === p());//1 true//2 true
以上的箭頭函數(shù)都是在方法內(nèi)部,總之都是以非方法的方式使用缘圈,如果將箭頭函數(shù)當(dāng)做一個(gè)方法使用會(huì)怎樣呢?
上例子
varobj = {i:10,b:()=>console.log(this.i,this),c:function(){console.log(this.i,this)? }}obj.b();// undefined window{...}obj.c();// 10 Object {...}
可以看到袜蚕,作為方法的箭頭函數(shù)this指向全局window對(duì)象糟把,而普通函數(shù)則指向調(diào)用它的對(duì)象
以上為個(gè)人學(xué)習(xí)整理內(nèi)容, 文中例子參考MDN牲剃, 歡迎交流學(xué)習(xí)