關於JavaScript的執行環境與做用域的解讀

關於JavaScript的執行環境與做用域的解讀javascript

JavaScript高級編程中關於執行環境與做用域的問題在第四章有過說起,可是交代的不是很明確,所以查閱了網上各類資料,對於執行環境以及做用域有了一個初步的認識。java

1、什麼是執行環境(execution context) 編程

執行環境在書中是這樣定義的:執行環境定義了變量或函數有權訪問的其餘數據,決定了他們各自的行爲。 瀏覽器

對於execution context的翻譯有兩種,一種是執行環境,一種是執行上下文。此處使用執行環境的概念。 ide

JavaScript中有如下三種運行環境: 函數

1.全局級的運行環境:默認的代碼運行環境,當代碼被載入的時候,JavaScript引擎默認進入該環境; this

2.函數級的運行環境:執行流進入函數的時候,運行函數中的代碼; spa

3.Eval函數的運行環境:執行Eval()函數內的代碼線程

2、什麼是做用域 翻譯

JavaScript中的做用域分爲全局做用域和函數做用域,目前不支持塊狀做用域。JavaScript的做用是靜態做用域也叫詞法做用域,意思就是做用域是靜態的肯定的。有一些說法常常將做用域與執行環境等同起來,我的的理解是這是兩個不一樣層次的概念,雖然二者在某些方面的描述着實有些類似,可是並不能等同起來說。做用域不光是針對JavaScript這門語言來講的,在其餘語言中也有這種概念。而執行環境是JavaScript解釋器在解釋執行的過程當中產生的這種概念。所以我的認爲不能等同來說。

3、什麼是執行環境棧

瀏覽器的JavaScript引擎是單線程,所以在某一個時刻只能有一個事件被激活處理,所以便產生了這樣一個執行環境棧。JavaScript的代碼被載入到後,JavaScript引擎默認進入全局執行環境,當在全局執行環境中調用一個函數的時候,程序的執行流會進入該函數,同時建立該函數的執行環境,而後將該函數的執行環境壓入執行環境棧的頂部。瀏覽器引擎在執行的時候老是在執行環境棧頂部的執行環境中執行。當頂部執行環境中的代碼被執行完畢後,該執行環境被從環境棧頂部彈出,而後在其下的執行環境中執行。這樣執行環境經歷一個不斷壓棧,彈棧的過程,直到最底部的全局執行環境。

4、執行環境的建立過程

由上面的描述咱們知道,執行環境是一個動態的概念,每當調用一個函數的時候,就會該函數建立一個執行環境。在JavaScript引擎內部這個過程分爲兩步:

建立階段

創建變量、函數、arguments對象、參數

創建做用域鏈

肯定this

執行階段

變量賦值、函數引用、執行其餘代碼

能夠把執行環境想象成一個對象,裏面包含了變量對象(函數中的arguments對象,參數,內部的變量以及函數聲明),做用域鏈(當前執行環境的變量對象加上父執行環境的變量對象)以及this的值。

5、執行環境對象建立過程詳解

根據上面的描述,執行環境對象的建立發生在函數被調用後,真正的函數體被執行之前。在這個過程當中JavaScript引擎會檢查函數的參數,變量的聲明以及內部的函數,根據得到這些信息建立執行環境對象。這個過程當中變量對象(variable object)、做用域鏈(scope chain)以及this所指定的對象都會被肯定。建立過程的具體步驟以下:

找到當前執行環境中被調用函數的代碼

被調用函數的函數體執行之前,建立被調用函數的執行環境

進入創建階段:

a.建立執行環境對象:

創建arguments對象、檢查當前執行環境中的參數、創建該對象的屬性及屬性值

檢查當前執行環境中的函數聲明:

每找到一個函數聲明,就在該執行環境對象下用該函數名創建一個屬性,屬性值就是一個執行該函數在內存中的一個地址的引用。(這就是一個變量提高的過程)若是上述屬性值已經在該執行環境對象中存在,就用新的引用值覆蓋以前的值(這就是爲何在JavaScript中不存在函數重載的緣由)

檢查當前執行環境中的變量聲明:

每找到一個變量聲明,就在該執行環境對象下用該變量名創建一個屬性,屬性值就是undefined。若是該屬性值已經存在的話,直接跳過(這就是爲何同名的變量沒法覆蓋同名的函數聲明的緣由),原屬性值不會被修改

b.初始化做用域鏈

c.肯定執行環境中this的指向

代碼執行階段

執行函數體中的代碼,給執行環境對象中的屬性賦值。

6、實例解析

<script type="text/javascript">

    (function(){

        console.log('execution context test');

        console.log(typeof foo);//function

        console.log(typeof a);//undefined

        console.log(typeof b);//undefined

        console.log(typeof c);//undefined

        function foo(){

            console.log('foo function');

        }

        var foo='test';

        var a='a';

        var b='b';

        var c=function(){

            console.log('c function');

        };

 

    })();

</script>

如上面程序實例所示,建立了一個自執行函數,代碼被加載後且代碼未被執行前自動建立一個執行環境的對象,而且分析執行環境的參數,而後查找對應的函數聲明以及變量聲明。這就是爲何在打印foo的類型的時候是function的緣由,解析變量聲明會被自動賦值爲undefindc爲一個函數表達式,所以也被解析爲變量,賦值爲undefind。在有foo函數聲明的前提下,代碼中又聲明瞭一個foo的變量,這種狀況下在解析變量聲明的過程當中,會發如今執行環境對象中有了一個foo的屬性,所以直接跳過。

相關文章
相關標籤/搜索