全局代碼的上下文環境數據內容爲:javascript
普通變量(包括函數表達式),java 如: var a = 10;函數 |
聲明(默認賦值爲undefined)this |
函數聲明,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未定義,結束;不然繼續;
第三步,(不是全局做用域,那就是函數做用域)將建立該函數的做用域做爲當前做用域;
第四步,跳轉到第一步。