老生常談原生JS執(zhí)行環(huán)境與作用域
首先,我們要知道執(zhí)行環(huán)境和作用域是兩個(gè)完全不同的概念。
函數(shù)的每次調(diào)用都有與之緊密相關(guān)的作用域和執(zhí)行環(huán)境。從根本上來(lái)說(shuō),作用域是基于函數(shù)的,而執(zhí)行環(huán)境是基于對(duì)象的(例如:全局執(zhí)行環(huán)境即window對(duì)象)。
換句話說(shuō),作用域涉及到所被調(diào)用函數(shù)中的變量訪問,并且不同的調(diào)用場(chǎng)景是不一樣的。執(zhí)行環(huán)境始終是this關(guān)鍵字的值,它是擁有當(dāng)前所執(zhí)行代碼的對(duì)象的引用。每個(gè)執(zhí)行環(huán)境都有一個(gè)與之關(guān)聯(lián)的變量對(duì)象,環(huán)境中定義的所有變量和函數(shù)都保存在這個(gè)對(duì)象中。雖然我們編寫的代碼無(wú)法訪問這個(gè)對(duì)象,但解析器在處理數(shù)據(jù)時(shí)會(huì)在后臺(tái)使用它。
執(zhí)行環(huán)境(也稱執(zhí)行上下文–execution context)
當(dāng)JavaScript解釋器初始化執(zhí)行代碼時(shí),它首先默認(rèn)進(jìn)入全局執(zhí)行環(huán)境,從此刻開始,函數(shù)的每次調(diào)用都會(huì)創(chuàng)建一個(gè)新的執(zhí)行環(huán)境。
每個(gè)函數(shù)都有自己的執(zhí)行環(huán)境。當(dāng)執(zhí)行流進(jìn)入一個(gè)函數(shù)時(shí),函數(shù)的環(huán)境就會(huì)被推入一個(gè)環(huán)境棧中(execution stack)。在函數(shù)執(zhí)行完后,棧將其環(huán)境彈出,把控制權(quán)返回給之前的執(zhí)行環(huán)境。ECMAScript程序中的執(zhí)行流正是由這個(gè)便利的機(jī)制控制著。
執(zhí)行環(huán)境可以分為創(chuàng)建和執(zhí)行兩個(gè)階段。在創(chuàng)建階段,解析器首先會(huì)創(chuàng)建一個(gè)變量對(duì)象(variable object,也稱為活動(dòng)對(duì)象 activation object),它由定義在執(zhí)行環(huán)境中的變量、函數(shù)聲明、和參數(shù)組成。在這個(gè)階段,作用域鏈會(huì)被初始化,this的值也會(huì)被最終確定。在執(zhí)行階段,代碼被解釋執(zhí)行。
Demo:
<script type="text/javascript">
function Fn1(){
function Fn2(){
alert(document.body.tagName);//BODY
//other code...
}
Fn2();
}
Fn1();
//code here
</script>

小結(jié)
當(dāng)javascript代碼被瀏覽器載入后,默認(rèn)最先進(jìn)入的是一個(gè)全局執(zhí)行環(huán)境。當(dāng)在全局執(zhí)行環(huán)境中調(diào)用執(zhí)行一個(gè)函數(shù)時(shí),程序流就進(jìn)入該被調(diào)用函數(shù)內(nèi),此時(shí)JS引擎就會(huì)為該函數(shù)創(chuàng)建一個(gè)新的執(zhí)行環(huán)境,并且將其壓入到執(zhí)行環(huán)境堆棧的頂部。瀏覽器總是執(zhí)行當(dāng)前在堆棧頂部的執(zhí)行環(huán)境,一旦執(zhí)行完畢,該執(zhí)行環(huán)境就會(huì)從堆棧頂部被彈出,然后,進(jìn)入其下的執(zhí)行環(huán)境執(zhí)行代碼。這樣,堆棧中的執(zhí)行環(huán)境就會(huì)被依次執(zhí)行并且彈出堆棧,直到回到全局執(zhí)行環(huán)境。
此外還要注意一下幾點(diǎn):
單線程
同步執(zhí)行
唯一的全局執(zhí)行環(huán)境
局部執(zhí)行環(huán)境的個(gè)數(shù)沒有限制
每次某個(gè)函數(shù)被調(diào)用,就會(huì)有個(gè)新的局部執(zhí)行環(huán)境為其創(chuàng)建,即使是多次調(diào)用的自身函數(shù)(即一個(gè)函數(shù)被調(diào)用多次,也會(huì)創(chuàng)建多個(gè)不同的局部執(zhí)行環(huán)境)。
作用域
當(dāng)代碼在一個(gè)環(huán)境中執(zhí)行時(shí),會(huì)創(chuàng)建變量對(duì)象的一個(gè)作用域鏈(scope chain)。作用域鏈的用途是保證對(duì)執(zhí)行環(huán)境有權(quán)訪問的所有變量和函數(shù)的有序訪問。
作用域鏈包含了執(zhí)行環(huán)境棧中的每個(gè)執(zhí)行環(huán)境對(duì)應(yīng)的變量對(duì)象。通過(guò)作用域鏈,可以決定變量的訪問和標(biāo)識(shí)符的解析。
注意:全局執(zhí)行環(huán)境的變量對(duì)象始終都是作用域鏈的最后一個(gè)對(duì)象。
在訪問變量時(shí),就必須存在一個(gè)可見性的問題(內(nèi)層環(huán)境可以訪問外層中的變量和函數(shù),而外層環(huán)境不能訪問內(nèi)層的變量和函數(shù))。更深入的說(shuō),當(dāng)訪問一個(gè)變量或調(diào)用一個(gè)函數(shù)時(shí),JavaScript引擎將不同執(zhí)行環(huán)境中的變量對(duì)象按照規(guī)則構(gòu)建一個(gè)鏈表,在訪問一個(gè)變量時(shí),先在鏈表的第一個(gè)變量對(duì)象上查找,如果沒有找到則繼續(xù)在第二個(gè)變量對(duì)象上查找,直到搜索到全局執(zhí)行環(huán)境的變量對(duì)象即window對(duì)象。這也就形成了Scope Chain的概念。

作用域鏈圖,清楚的表達(dá)了執(zhí)行環(huán)境與作用域的關(guān)系(一一對(duì)應(yīng)的關(guān)系),作用域與作用域之間的關(guān)系(鏈表結(jié)構(gòu),由上至下的關(guān)系)。
Demo:
var color = "blue";
function changeColor(){
var anotherColor = "red";
function swapColors(){
var tempColor = anotherColor;
anotherColor = color;
color = tempColor;
// 這里可以訪問color, anotherColor, 和 tempColor
}
// 這里可以訪問color 和 anotherColor,但是不能訪問 tempColor
swapColors();
}
changeColor();
// 這里只能訪問color
console.log("Color is now " + color);
上述代碼一共包括三個(gè)執(zhí)行環(huán)境:全局執(zhí)行環(huán)境、changeColor()的局部執(zhí)行環(huán)境、swapColors()的局部執(zhí)行環(huán)境。
全局環(huán)境有一個(gè)變量color和一個(gè)函數(shù)changecolor();
changecolor()函數(shù)的局部環(huán)境中具有一個(gè)anothercolor屬性和一個(gè)swapcolors函數(shù),當(dāng)然,changecolor函數(shù)中可以訪問自身以及它外圍(即全局環(huán)境)中的變量;
swapcolor()函數(shù)的局部環(huán)境中具有一個(gè)變量tempcolor。在該函數(shù)內(nèi)部可以訪問上面的兩個(gè)環(huán)境(changecolor和window)中的所有變量,因?yàn)槟莾蓚€(gè)環(huán)境都是它的父執(zhí)行環(huán)境。
上述代碼的作用域鏈如下圖所示:

從上圖發(fā)現(xiàn)。內(nèi)部環(huán)境可以通過(guò)作用域鏈訪問所有的外部環(huán)境,但是外部環(huán)境不能訪問內(nèi)部環(huán)境中的任何變量和函數(shù)。
標(biāo)識(shí)符解析(變量名或函數(shù)名搜索)是沿著作用域鏈一級(jí)一級(jí)地搜索標(biāo)識(shí)符的過(guò)程。搜索過(guò)程始終從作用域鏈的前端開始,然后逐級(jí)地向后(全局執(zhí)行環(huán)境)回溯,直到找到標(biāo)識(shí)符為止。
執(zhí)行環(huán)境與作用域的區(qū)別與聯(lián)系
執(zhí)行環(huán)境為全局執(zhí)行環(huán)境和局部執(zhí)行環(huán)境,局部執(zhí)行環(huán)境是函數(shù)執(zhí)行過(guò)程中創(chuàng)建的。
作用域鏈?zhǔn)腔趫?zhí)行環(huán)境的變量對(duì)象的,由所有執(zhí)行環(huán)境的變量對(duì)象(對(duì)于函數(shù)而言是活動(dòng)對(duì)象,因?yàn)樵诤瘮?shù)執(zhí)行環(huán)境中,變量對(duì)象是不能直接訪問的,此時(shí)由活動(dòng)對(duì)象(activation object,縮寫為AO)扮演VO(變量對(duì)象)的角色。)共同組成。
當(dāng)代碼在一個(gè)環(huán)境中執(zhí)行時(shí),會(huì)創(chuàng)建變量對(duì)象的一個(gè)作用域鏈。作用域鏈的用途:是保證對(duì)執(zhí)行環(huán)境有權(quán)訪問的所有變量和函數(shù)的有序訪問。作用域鏈的前端,始終都是當(dāng)前執(zhí)行的代碼所在環(huán)境的變量對(duì)象。
小練習(xí)
<script type="text/javascript">
(function(){
a= 5;
console.log(window.a);//undefined
var a = 1;//這里會(huì)發(fā)生變量聲明提升
console.log(a);//1
})();
</script>
window.a之所以是undefined,是因?yàn)関ar a = 1;發(fā)生了變量聲明提升。相當(dāng)于如下代碼:
<script type="text/javascript">
(function(){
var a;//a是局部變量
a = 5;//這里局部環(huán)境中有a,就不會(huì)找全局中的
console.log(window.a);//undefined
a = 1;//這里會(huì)發(fā)生變量聲明提升
console.log(a);//1
})();
</script>
以上就是小編為大家?guī)?lái)的老生常談原生JS執(zhí)行環(huán)境與作用域全部?jī)?nèi)容了,希望大家多多支持腳本之家~
- js 函數(shù)的執(zhí)行環(huán)境和作用域鏈的深入解析
- 深入Javascript函數(shù)、遞歸與閉包(執(zhí)行環(huán)境、變量對(duì)象與作用域鏈)使用詳解
- 淺談javascript中執(zhí)行環(huán)境(作用域)與作用域鏈
- javascript作用域鏈與執(zhí)行環(huán)境詳解
- javascript基礎(chǔ)進(jìn)階_深入剖析執(zhí)行環(huán)境及作用域鏈
- javascript中關(guān)于執(zhí)行環(huán)境的雜談
- 談一談js中的執(zhí)行環(huán)境及作用域
- javascript執(zhí)行環(huán)境及作用域詳解
- 淺談JavaScript 執(zhí)行環(huán)境、作用域及垃圾回收
- JavaScript執(zhí)行環(huán)境及作用域鏈實(shí)例分析
相關(guān)文章
談?wù)劄槭裁茨愕?JavaScript 代碼如此冗長(zhǎng)
這篇文章主要介紹了談?wù)劄槭裁茨愕?JavaScript 代碼如此冗長(zhǎng),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-01-01
純js網(wǎng)頁(yè)畫板(Graphics)類簡(jiǎn)介及實(shí)現(xiàn)代碼
今天需要在網(wǎng)頁(yè)上畫一個(gè)圖譜,想到用JS,經(jīng)過(guò)學(xué)習(xí),和網(wǎng)上搜索,經(jīng)過(guò)整理優(yōu)化得到下面代碼,注意不是用HTML5的canvas,而是用的純js,需要了解的朋友可以參考下2012-12-12
微信公眾平臺(tái)獲取access_token的方法步驟
這篇文章主要介紹了微信公眾平臺(tái)獲取access_token的方法步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03
js+html實(shí)現(xiàn)周歲年齡計(jì)算器
這篇文章主要為大家詳細(xì)介紹了js+html實(shí)現(xiàn)周歲年齡計(jì)算器的方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-06-06
JS學(xué)習(xí)筆記之?dāng)?shù)組去重實(shí)現(xiàn)方法小結(jié)
這篇文章主要介紹了JS學(xué)習(xí)筆記之?dāng)?shù)組去重實(shí)現(xiàn)方法,結(jié)合實(shí)例形式總結(jié)分析了javascript數(shù)組去重的5種常見實(shí)現(xiàn)方法及相關(guān)操作技巧,需要的朋友可以參考下2019-05-05
JS利用正則表達(dá)式實(shí)現(xiàn)簡(jiǎn)單的密碼強(qiáng)弱判斷實(shí)例
這篇文章主要給大家介紹了關(guān)于JS利用正則表達(dá)式實(shí)現(xiàn)簡(jiǎn)單的密碼強(qiáng)弱判斷的相關(guān)資料,實(shí)現(xiàn)后的效果非常簡(jiǎn)單,但也挺實(shí)用的,文中給出了詳細(xì)的示例代碼供大家參考學(xué)習(xí),需要的朋友們下面來(lái)一起看看吧。2017-06-06
教學(xué)演示-UBB,剪貼板,textRange及其他
教學(xué)演示-UBB,剪貼板,textRange及其他...2006-07-07
JavaScript創(chuàng)建對(duì)象的四種常用模式實(shí)例分析
這篇文章主要介紹了JavaScript創(chuàng)建對(duì)象的四種常用模式,結(jié)合實(shí)例形式分析了javascript使用工廠模式、構(gòu)造函數(shù)模式、原型模式及動(dòng)態(tài)原型模式創(chuàng)建對(duì)象的相關(guān)操作技巧與注意事項(xiàng),需要的朋友可以參考下2019-01-01

