摘要:全局執(zhí)行環(huán)境的變量對象始終是作用域鏈中的最后一個變量對象。綜上,每個函數(shù)對應(yīng)一個執(zhí)行環(huán)境,每個執(zhí)行環(huán)境對應(yīng)一個變量對象,而多個變量對象構(gòu)成了作用域鏈,如果當(dāng)前執(zhí)行環(huán)境是函數(shù),那么其活動對象在作用域鏈的前端。
1.幾個概念
先說幾個概念:函數(shù)、執(zhí)行環(huán)境、變量對象、作用域鏈、活動對象。這幾個東東之間有什么關(guān)系呢,往下看~
函數(shù)函數(shù)大家都知道,我想說的是,js中,在函數(shù)內(nèi)部有兩個特殊的對象:arguments 和 this 。 arguments 是一個類數(shù)組對象,包含著傳入函數(shù)中的所有參數(shù)。 this 引用的是函數(shù)據(jù)以執(zhí)行的環(huán)境對象。
執(zhí)行環(huán)境在《js高級程序設(shè)計》中,是這樣定義的:
執(zhí)行環(huán)境定義了變量或函數(shù)有權(quán)訪問的其他數(shù)據(jù),決定了它們各自的行為。
這里要特別提到一個執(zhí)行環(huán)境——全局執(zhí)行環(huán)境。全局執(zhí)行環(huán)境是最外圍的執(zhí)行環(huán)境,在web瀏覽器中,全局執(zhí)行環(huán)境被認(rèn)為是window對象。全局執(zhí)行環(huán)境會一直存在于環(huán)境棧的最底端,直到關(guān)閉網(wǎng)頁或者瀏覽器。
執(zhí)行環(huán)境也叫執(zhí)行上下文。
《js高級程序設(shè)計》定義如下:
每個執(zhí)行環(huán)境都有一個與之關(guān)聯(lián)的變量對象,環(huán)境中定義的所有變量和函數(shù)都保存在這個對象中。
由上可以看出兩點:1.執(zhí)行環(huán)境和變量對象是一一對應(yīng)的。2.執(zhí)行環(huán)境其實是一個“虛”的概念,而變量對象是實際存在的對象,能夠被解析器訪問到。不嚴(yán)謹(jǐn)?shù)恼f,為了訪問執(zhí)行環(huán)境,就創(chuàng)造了變量對象這個東東,通過變量對象就可以訪問執(zhí)行環(huán)境中的所有變量和函數(shù)了,它倆其實是一個東西,只不過一個是虛的,一個是真實存在的。
當(dāng)一個執(zhí)行環(huán)境中的所有代碼執(zhí)行完畢后,該執(zhí)行環(huán)境被銷毀,保存在其中的所有變量和函數(shù)定義也隨之銷毀。其實是這個執(zhí)行環(huán)境對應(yīng)的變量對象被銷毀。發(fā)現(xiàn)很多地方都把執(zhí)行環(huán)境和變量對象混談,大家似乎很少提到變量對象,都用 “執(zhí)行環(huán)境” 這四個字替代了,把執(zhí)行環(huán)境說成了一個對象,沒辦法,誰讓說的是一個東西呢。
變量對象補(bǔ)充當(dāng)JS執(zhí)行流進(jìn)入函數(shù)時,JavaScript引擎在內(nèi)部創(chuàng)建一個對象,叫做Variable Object。
對應(yīng)函數(shù)的每一個參數(shù),在Variable Object上添加一個屬性,屬性的名字、值與參數(shù)的名字、值相同。
函數(shù)中每聲明一個變量,也會在Variable Object上添加一個屬性,名字就是變量名,因此為變量賦值就是給Variable Object對應(yīng)的屬性賦值。
在函數(shù)中訪問參數(shù)或者局部變量時,就是在variable Object上搜索相應(yīng)的屬性,返回其值。
一般情況下Variable Object是一個內(nèi)部對象,JS代碼中無法直接訪問。
作用域鏈的用途:保證對執(zhí)行環(huán)境有權(quán)訪問的所有變量和函數(shù)的 有序 訪問。
當(dāng)代碼在一個執(zhí)行環(huán)境中執(zhí)行時,就會創(chuàng)建變量對象的一個作用域鏈。
我的理解是,作用域鏈?zhǔn)怯梢粋€一個變量對象鏈接起來的一個鏈,整個作用域鏈構(gòu)成了當(dāng)前執(zhí)行環(huán)境中變量和函數(shù)可訪問的范圍,即作用域。由于變量對象是按一定順序鏈接在一起的,所以就達(dá)到了對所有可訪問變量、函數(shù)有序訪問的效果。那么它們是按怎樣的順序鏈接成作用域鏈的呢?這就要說到最后一個概念——活動對象。
活動對象當(dāng)函數(shù)運行時就會為其創(chuàng)建一個活動對象,其中包含形參和函數(shù)特殊的arguments對象?;顒訉ο笾髸鰹楹瘮?shù)執(zhí)行環(huán)境的變量對象來使用。
回到之前的問題,作用域鏈中的變量對象是如何排序的呢?
作用域鏈的前端,始終都是當(dāng)前執(zhí)行的代碼所在環(huán)境的變量對象。但如果這個環(huán)境是函數(shù),則將其活動對象作為變量對象,放在其作用域鏈的前端。作用域鏈中的下一個變量對象來自包含環(huán)境,而再下一個變量對象來自下一個包含環(huán)境……這樣一直延續(xù)到全局執(zhí)行環(huán)境。全局執(zhí)行環(huán)境的變量對象始終是作用域鏈中的最后一個變量對象。
為什么執(zhí)行環(huán)境是函數(shù)會有這樣特殊的規(guī)定呢?
《JS權(quán)威指南》中有一句很精辟的描述:
JavaScript中的函數(shù)運行在它們被定義的作用域里,而不是它們被執(zhí)行的作用域里。
按照之前所說,在函數(shù)定義的作用域里,當(dāng)前執(zhí)行環(huán)境的作用域鏈上是沒有該函數(shù)的活動對象的,為了訪問函數(shù)內(nèi)部的變量、函數(shù),所以要將其活動對象插在當(dāng)前作用域鏈的前端。
2.它們之間的關(guān)系每個函數(shù)都有自己的執(zhí)行環(huán)境,當(dāng)執(zhí)行流進(jìn)入一個函數(shù)時,函數(shù)的環(huán)境就會被推入一個環(huán)境棧中。而在執(zhí)行后,棧將其環(huán)境彈出,把控制權(quán)返回給之前的執(zhí)行環(huán)境。
綜上,每個函數(shù)對應(yīng)一個執(zhí)行環(huán)境,每個執(zhí)行環(huán)境對應(yīng)一個變量對象,而多個變量對象構(gòu)成了作用域鏈,如果當(dāng)前執(zhí)行環(huán)境是函數(shù),那么其活動對象在作用域鏈的前端。
3.其他補(bǔ)充JS的語法風(fēng)格和 C/C++ 類似, 但作用域的實現(xiàn)卻和 C/C++ 不同,并非用“堆棧”方式,而是使用列表,具體過程如下(ECMA262中所述):
任何執(zhí)行上下文時刻的作用域, 都是由作用域鏈(scope chain)來實現(xiàn).
在執(zhí)行func的定義語句的時候, 會創(chuàng)建一個這個函數(shù)對象的[[scope]]屬性(內(nèi)部屬性,只有JS引擎可以訪問),并將這個[[scope]]屬性鏈接到定義它的作用域鏈上。
在調(diào)用func的時候, 會創(chuàng)建一個活動對象,然后將調(diào)用參數(shù)賦值給形參數(shù),對于缺少的調(diào)用參數(shù),賦值為undefined。然后將這個活動對象做為scope chain的最前端, 并將func的[[scope]]屬性所指向的,定義func時候的頂級活動對象,加入到scope chain.
《js高級程序設(shè)計》 P73 4.2 執(zhí)行環(huán)境及作用域
鳥哥:JavaScript作用域原理
JavaScript 開發(fā)進(jìn)階:理解 JavaScript 作用域和作用域鏈
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/91506.html
摘要:閉包是怎么通過作用域鏈霸占更多內(nèi)存的本文是作者學(xué)習(xí)高級程序設(shè)計第一小節(jié)的一點個人理解,詳細(xì)教程請參考原教材。函數(shù)執(zhí)行過程創(chuàng)建了一個函數(shù)的活動對象,作用域鏈的最前端指向這個對象。函數(shù)執(zhí)行完畢返回值后執(zhí)行環(huán)境作用域鏈和活動對象一并銷毀。 JavaScript 閉包是怎么通過作用域鏈霸占更多內(nèi)存的? 本文是作者學(xué)習(xí)《JavaScript 高級程序設(shè)計》7.2第一小節(jié)的一點個人理解,詳細(xì)教程請...
摘要:關(guān)于作用域?qū)崿F(xiàn)的描述任何執(zhí)行上下文時刻的作用域,都是由作用域鏈來實現(xiàn)的。在一個函數(shù)被定義的時候,會將它此時的作用域鏈鏈接到這個函數(shù)對象的屬性。參考資料鳥哥作用域原理理解作用域和作用域鏈阮一峰老師微博上的關(guān)于作用域的一道題 javascript作用域原理學(xué)習(xí) 在每次調(diào)用一個函數(shù)的時候,就會進(jìn)入一個函數(shù)內(nèi)的作用域,當(dāng)從函數(shù)返回 以后,就會返回調(diào)用前的作用域。 ECMA262關(guān)于作...
摘要:但是,必須強(qiáng)調(diào),閉包是一個運行期概念。通過原型鏈可以實現(xiàn)繼承,而與閉包相關(guān)的就是作用域鏈。常理來說,一個函數(shù)執(zhí)行完畢,其執(zhí)行環(huán)境的作用域鏈會被銷毀。所以此時,的作用域鏈雖然銷毀了,但是其活動對象仍在內(nèi)存中。 學(xué)習(xí)Javascript閉包(Closure)javascript的閉包JavaScript 閉包深入理解(closure)理解 Javascript 的閉包JavaScript ...
摘要:每一個運行期上下文都和一個作用域鏈關(guān)聯(lián)。這個對象將被推入作用域鏈的頭部,這意味著函數(shù)的所有局部變量現(xiàn)在處于第二個作用域鏈對象中,因此訪問代價更高了。在代碼塊內(nèi)部,函數(shù)的所有局部變量將會被放在第二個作用域鏈對象中。 參考: Javascript作用域原理 理解 JavaScript 作用域和作用域鏈 JavaScript 作用域 作用域就是變量與函數(shù)的可訪問范圍,即作用域控制著變量與函數(shù)...
摘要:我們再來看一下第一段代碼小紅小黑腳本出錯腳本出錯在這段代碼中變量與函數(shù),都擁有局部作用域。作用域鏈的最前端,始終都是當(dāng)前執(zhí)行代碼所在的作用域的變量對象。 個人博客原址 無論什么語言中,作用域都是一個十分重要的概念,在JavaScript中也不例外,作用域定義了變量或者函數(shù)有權(quán)訪問的范圍,決定了它們各自的行為。要理解JavaScript中的作用域首先就要知道:在let出現(xiàn)之前,JS中變...
閱讀 3280·2021-11-24 09:38
閱讀 2154·2021-11-23 09:51
閱讀 1745·2021-10-13 09:39
閱讀 2620·2021-09-23 11:53
閱讀 1405·2021-09-02 15:40
閱讀 3656·2019-08-30 15:54
閱讀 1131·2019-08-30 13:04
閱讀 2563·2019-08-30 11:01