執行上下文和做用域,做用域鏈

  • 給執行上下文環境下一個通俗的定義——在執行代碼以前,把將要用到的全部的變量都事先拿出來,有的直接賦值了,有的先用undefined佔個空。(變量的值是在執行過程當中產生的肯定的)
  •  javascript除了全局做用域以外,只有函數對象不建立做用域能夠建立的做用域。(ES6新加了塊級做用域 let)

 

全局代碼的上下文環境數據內容爲:javascript

 

普通變量(包括函數表達式),java

如: var a = 10;函數

聲明(默認賦值爲undefinedthis

函數聲明,spa

如: function fn() { }code

賦值對象

thisblog

賦值ip

 

若是代碼段是函數體,那麼在此基礎上須要附加:作用域

 

參數

賦值

arguments

賦值

自由變量的取值做用域

賦值

做用域在函數定義時就已經肯定了,而不是在函數調用時肯定。做用域有上下級的關係,上下級關係的肯定就看函數是在哪一個做用域下建立的。例如,fn做用域下建立了bar函數,那麼「fn做用域」就是「bar做用域」的上級。做用域最大的用處就是隔離變量,不一樣做用域下同名變量不會有衝突。

 

 

 

做用域只是一個「地盤」,一個抽象的概念,其中沒有變量。要經過做用域對應的執行上下文環境來獲取變量的值。

 

一個例子:

for (var i = 0; i < 5; i++) {
    setTimeout(function() {
        console.log( i);
    }, 1000);
}

console.log(i);

若是咱們約定,用箭頭表示其先後的兩次輸出之間有 1 秒的時間間隔,而逗號表示其先後的兩次輸出之間的時間間隔能夠忽略,代碼實際運行的結果該如何描述?

結果是5->5,5,5,5,5。由於i是個自由變量,當循環結束時,i已經等於5了,而setTimeout中的函數還未執行,等其執行時,獲取的i爲5。

若想輸出5->0,1,2,3,4,利用 JS 中基本類型(Primitive Type)的參數傳遞是按值傳遞(Pass by Value)的特徵便可。

方法一:IIFE

for (var i = 0; i < 5; i++) {
    (function(j) {  // j = i
        setTimeout(function() {
            console.log(j);
        }, 1000);
    })(i);
}

console.log( i);

方法二:

 

var output = function (i) {
    setTimeout(function() {
        console.log(i);
    }, 1000);
};

for (var i = 0; i < 5; i++) {
    output(i);  // 這裏傳過去的 i 值被複制了
}

console.log(i);

 

自由變量:

在A做用域中使用的變量x,卻沒有在A做用域中聲明(即在其餘做用域中聲明的),對於A做用域來講,x就是一個自由變量。以下圖 

如上程序中,在調用fn()函數時,函數體中第6行。取b的值就直接能夠在fn做用域中取,由於b就是在這裏定義的。而取x的值時,就須要到另外一個做用域中取。到哪一個做用域中取呢?

要到建立這個函數的那個做用域中取值——是「建立」,而不是「調用」,—其實這就是所謂的「靜態做用域」。

做用域鏈:

 

上面描述的只是跨一步做用域去尋找。

 

若是跨了一步,還沒找到呢?——接着跨!——一直跨到全局做用域爲止。要是在全局做用域中都沒有找到,那就是真的沒有了。

 

這個一步一步「跨」的路線,咱們稱之爲——做用域鏈。

咱們拿文字總結一下取自由變量時的這個「做用域鏈」過程:(假設a是自由量)

第一步,如今當前做用域查找a,若是有則獲取並結束。若是沒有則繼續;

第二步,若是當前做用域是全局做用域,則證實a未定義,結束;不然繼續;

第三步,(不是全局做用域,那就是函數做用域)將建立該函數的做用域做爲當前做用域;

第四步,跳轉到第一步。

相關文章
相關標籤/搜索