深入理解函數(shù)執(zhí)行上下文 this
JavaScript 中的 this 是什么
關(guān)于 this,我們得先從執(zhí)行上下文說(shuō)起。我們知道:執(zhí)行上下文中包含了變量環(huán)境、詞法環(huán)境、外部環(huán)境,當(dāng)然也包括 this,具體你可以參考下圖:

從圖中可以看出,this 是和執(zhí)行上下文綁定的,也就是說(shuō)每個(gè)執(zhí)行上下文中都有一個(gè) this。執(zhí)行上下文主要分為三種——全局執(zhí)行上下文、函數(shù)執(zhí)行上下文和 eval 執(zhí)行上下文,所以對(duì)應(yīng)的 this 也只有這三種——全局執(zhí)行上下文中的 this、函數(shù)中的 this 和 eval 中的 this。
不過(guò)由于 eval 我們使用的不多,所以本文我們對(duì)此就不做介紹了,如果你感興趣的話,可以自行搜索和學(xué)習(xí)相關(guān)知識(shí)。
那么接下來(lái)我們就重點(diǎn)講解下全局執(zhí)行上下文中的 this和函數(shù)執(zhí)行上下文中的 this。
全局執(zhí)行上下文中的 this
首先我們來(lái)看看全局執(zhí)行上下文中的 this 是什么。
你可以在控制臺(tái)中輸入console.log(this)來(lái)打印出來(lái)全局執(zhí)行上下文中的 this,最終輸出的是 window 對(duì)象。所以你可以得出這樣一個(gè)結(jié)論:全局執(zhí)行上下文中的 this 是指向 window 對(duì)象的。這也是 this 和作用域鏈的唯一交點(diǎn),作用域鏈的最底端包含了 window 對(duì)象,全局執(zhí)行上下文中的 this 也是指向 window 對(duì)象。
函數(shù)執(zhí)行上下文中的 this
現(xiàn)在你已經(jīng)知道全局對(duì)象中的 this 是指向 window 對(duì)象了,那么接下來(lái),我們就來(lái)重點(diǎn)分析函數(shù)執(zhí)行上下文中的 this。還是先看下面這段代碼:
function foo() {
console.log(this);
}
foo();
我們?cè)?foo 函數(shù)內(nèi)部打印出來(lái) this 值,執(zhí)行這段代碼,打印出來(lái)的也是 window 對(duì)象,這說(shuō)明在默認(rèn)情況下調(diào)用一個(gè)函數(shù),其執(zhí)行上下文中的 this 也是指向 window 對(duì)象的。估計(jì)你會(huì)好奇,那能不能設(shè)置執(zhí)行上下文中的 this 來(lái)指向其他對(duì)象呢?答案是肯定的。通常情況下,有下面三種方式來(lái)設(shè)置函數(shù)執(zhí)行上下文中的 this 值。
1. 通過(guò)函數(shù)的 call 方法設(shè)置
你可以通過(guò)函數(shù)的call方法來(lái)設(shè)置函數(shù)執(zhí)行上下文的 this 指向,比如下面這段代碼,我們就并沒(méi)有直接調(diào)用 foo 函數(shù),而是調(diào)用了 foo 的 call 方法,并將 bar 對(duì)象作為 call 方法的參數(shù)。
let bar = {
myName: " name1 ",
test1: 1,
};
function foo() {
this.myName = " name2 ";
}
foo.call(bar);
console.log(bar);
console.log(myName);
執(zhí)行這段代碼,然后觀察輸出結(jié)果,你就能發(fā)現(xiàn) foo 函數(shù)內(nèi)部的 this 已經(jīng)指向了 bar 對(duì)象,因?yàn)橥ㄟ^(guò)打印 bar 對(duì)象,可以看出 bar 的 myName 屬性已經(jīng)由“name1”變?yōu)?ldquo;name2”了,同時(shí)在全局執(zhí)行上下文中打印 myName,JavaScript 引擎提示該變量未定義。
其實(shí)除了 call 方法,你還可以使用bind和apply方法來(lái)設(shè)置函數(shù)執(zhí)行上下文中的 this,僅僅是語(yǔ)法稍有不同。
2. 通過(guò)對(duì)象調(diào)用方法設(shè)置
要改變函數(shù)執(zhí)行上下文中的 this 指向,除了通過(guò)函數(shù)的 call 方法來(lái)實(shí)現(xiàn)外,還可以通過(guò)對(duì)象調(diào)用的方式,比如下面這段代碼:
var myObj = {
name: " name ",
showThis: function () {
console.log(this);
},
};
myObj.showThis();
在這段代碼中,我們定義了一個(gè) myObj 對(duì)象,該對(duì)象是由一個(gè) name 屬性和一個(gè) showThis 方法組成的,然后再通過(guò) myObj 對(duì)象來(lái)調(diào)用 showThis 方法。執(zhí)行這段代碼,你可以看到,最終輸出的 this 值是指向 myObj 的。
所以,你可以得出這樣的結(jié)論:使用對(duì)象來(lái)調(diào)用其內(nèi)部的一個(gè)方法,該方法的 this 是指向?qū)ο蟊旧淼摹?/p>
其實(shí),你也可以認(rèn)為 JavaScript 引擎在執(zhí)行myObject.showThis()時(shí),將其轉(zhuǎn)化為了:
myObj.showThis.call(myObj)
接下來(lái)我們稍微改變下調(diào)用方式,把 showThis 賦給一個(gè)全局對(duì)象,然后再調(diào)用該對(duì)象,代碼如下所示:
var myObj = {
name: " time ",
showThis: function () {
this.name = " bang ";
console.log(this);
},
};
var foo = myObj.showThis;
foo();
執(zhí)行這段代碼,你會(huì)發(fā)現(xiàn) this 又指向了全局 window 對(duì)象。
所以通過(guò)以上兩個(gè)例子的對(duì)比,你可以得出下面這樣兩個(gè)結(jié)論:
- 在全局環(huán)境中調(diào)用一個(gè)函數(shù),函數(shù)內(nèi)部的 this 指向的是全局變量 window。
- 通過(guò)一個(gè)對(duì)象來(lái)調(diào)用其內(nèi)部的一個(gè)方法,該方法的執(zhí)行上下文中的 this 指向?qū)ο蟊旧怼?/li>
3. 通過(guò)構(gòu)造函數(shù)中設(shè)置
你可以像這樣設(shè)置構(gòu)造函數(shù)中的 this,如下面的示例代碼:
function CreateObj() {
this.name = " time ";
}
var myObj = new CreateObj();
在這段代碼中,我們使用 new 創(chuàng)建了對(duì)象 myObj,那你知道此時(shí)的構(gòu)造函數(shù) CreateObj 中的 this 到底指向了誰(shuí)嗎?
其實(shí),當(dāng)執(zhí)行 new CreateObj() 的時(shí)候,JavaScript 引擎做了如下四件事:
- 首先創(chuàng)建了一個(gè)空對(duì)象 tempObj;
- 接著調(diào)用 CreateObj.call 方法,并將 tempObj 作為 call 方法的參數(shù),這樣當(dāng) CreateObj 的執(zhí)行上下文創(chuàng)建時(shí),它的 this 就指向了 tempObj 對(duì)象;
- 然后執(zhí)行 CreateObj 函數(shù),此時(shí)的 CreateObj 函數(shù)執(zhí)行上下文中的 this 指向了 tempObj 對(duì)象;
- 最后返回 tempObj 對(duì)象。
這樣,我們就通過(guò) new 關(guān)鍵字構(gòu)建好了一個(gè)新對(duì)象,并且構(gòu)造函數(shù)中的 this 其實(shí)就是新對(duì)象本身。
this 的設(shè)計(jì)缺陷以及應(yīng)對(duì)方案
就我個(gè)人而言,this 并不是一個(gè)很好的設(shè)計(jì),因?yàn)樗暮芏嗍褂梅椒ǘ紱_擊人的直覺(jué),在使用過(guò)程中存在著非常多的坑。下面咱們就來(lái)一起看看那些 this 設(shè)計(jì)缺陷。
1. 嵌套函數(shù)中的 this 不會(huì)從外層函數(shù)中繼承
我認(rèn)為這是一個(gè)嚴(yán)重的設(shè)計(jì)錯(cuò)誤,并影響了很多開(kāi)發(fā)者。
至于如何解決?你可以在函數(shù)中聲明一個(gè)變量 self 用來(lái)保存 this。當(dāng)然也可以使用 ES6 中的箭頭函數(shù)來(lái)解決這個(gè)問(wèn)題。
2. 普通函數(shù)中的 this 默認(rèn)指向全局對(duì)象 window
上面我們已經(jīng)介紹過(guò)了,在默認(rèn)情況下調(diào)用一個(gè)函數(shù),其執(zhí)行上下文中的 this 是默認(rèn)指向全局對(duì)象 window 的。
不過(guò)這個(gè)設(shè)計(jì)也是一種缺陷,因?yàn)樵趯?shí)際工作中,我們并不希望函數(shù)執(zhí)行上下文中的 this 默認(rèn)指向全局對(duì)象,因?yàn)檫@樣會(huì)打破數(shù)據(jù)的邊界,造成一些誤操作。如果要讓函數(shù)執(zhí)行上下文中的 this 指向某個(gè)對(duì)象,最好的方式是通過(guò) call 方法來(lái)顯示調(diào)用。
這個(gè)問(wèn)題可以通過(guò)設(shè)置 JavaScript 的“嚴(yán)格模式”來(lái)解決。在嚴(yán)格模式下,默認(rèn)執(zhí)行一個(gè)函數(shù),其函數(shù)的執(zhí)行上下文中的 this 值是 undefined,這就解決上面的問(wèn)題了。
總結(jié)
回顧下內(nèi)容:
首先,在使用 this 時(shí),為了避坑,你要謹(jǐn)記以下三點(diǎn):
- 當(dāng)函數(shù)作為對(duì)象的方法調(diào)用時(shí),函數(shù)中的 this 就是該對(duì)象;
- 當(dāng)函數(shù)被正常調(diào)用時(shí),在嚴(yán)格模式下,this 值是 undefined,非嚴(yán)格模式下 this 指向的是全局對(duì)象 window;
- 嵌套函數(shù)中的 this 不會(huì)繼承外層函數(shù)的 this 值。
最后,我們還提了一下箭頭函數(shù),因?yàn)榧^函數(shù)沒(méi)有自己的執(zhí)行上下文,所以箭頭函數(shù)的 this 就是它外層函數(shù)的 this。
以上就是深入理解函數(shù)執(zhí)行上下文 this的詳細(xì)內(nèi)容,更多關(guān)于函數(shù)執(zhí)行上下文 this的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
微信小程序中input標(biāo)簽詳解及簡(jiǎn)單實(shí)例
這篇文章主要介紹了微信小程序中input標(biāo)簽詳解及簡(jiǎn)單實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-05-05
13個(gè)JavaScript 一行程序,讓你看起來(lái)就是個(gè)專家
JavaScript 可以做很多好玩的事, 從復(fù)雜的框架到處理API,有太多的東西需要學(xué)習(xí)。但是,它也能讓我們只用一行就能做一些了不起的事情。今天的文章小編就為大家介紹13 個(gè)JavaScript 行程序,需要的朋友可以參考下2021-08-08
requestAnimationFrame定時(shí)動(dòng)畫(huà)屏幕刷新率節(jié)流示例淺析
這篇文章主要為大家介紹了requestAnimationFrame定時(shí)動(dòng)畫(huà)屏幕刷新率節(jié)流示例淺析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02
Meta開(kāi)源JavaScript內(nèi)存泄漏監(jiān)測(cè)工具M(jìn)emLab安裝使用
這篇文章主要為大家介紹了Meta開(kāi)源JavaScript內(nèi)存泄漏監(jiān)測(cè)工具M(jìn)emLab安裝使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09
微信小程序之滾動(dòng)視圖容器的實(shí)現(xiàn)方法
這篇文章主要介紹了微信小程序之滾動(dòng)視圖容器的實(shí)現(xiàn)方法的相關(guān)資料,希望通過(guò)本文能幫助到大家,讓大家掌握這部分內(nèi)容,需要的朋友可以參考下2017-09-09
Skypack布局前端基建實(shí)現(xiàn)過(guò)程詳解
這篇文章主要為大家介紹了Skypack布局前端基建過(guò)程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07
微信小程序 省市區(qū)選擇器實(shí)例詳解(附源碼下載)
這篇文章主要介紹了微信小程序 省市區(qū)選擇器實(shí)例詳解的相關(guān)資料,區(qū)域間手勢(shì)滑動(dòng)切換,標(biāo)題欄高亮隨之切換,反之亦然;當(dāng)前選中標(biāo)題紅色高亮;回到前一級(jí)點(diǎn)擊某區(qū)域后,清空子代的區(qū)域,需要的朋友可以參考下2017-01-01
dotenv源碼解讀從.env文件中讀取環(huán)境變量
這篇文章主要為大家介紹了dotenv源碼解讀從.env文件中讀取環(huán)境變量示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12

