js便籤筆記(12)——瀏覽TOM大叔部落格的學習筆記 part2

王福朋發表於2014-09-01

1. 前言

昨天寫了《js便籤筆記(11)——瀏覽TOM大叔部落格的學習筆記 part1》,簡單記錄了幾個問題。part1的重點還是在於最後那個迴圈建立函式的問題,也就是多個子函式公用一個閉包資料的問題。如果覺得有興趣,可以再重新翻出來看看。

今天繼續把剩下的問題寫完。

2. 作用域鏈

學js的人,即使初級入門的也都知道“原型鏈”,但是“作用域鏈”,可能好多人沒有聽說過。大部分人都知道或者聽說過“閉包”,但是可能有好多人不知道閉包其實和作用域鏈有莫大的聯絡。如果理解閉包不從作用域鏈開始理解,那麼你就只能理解閉包的皮毛。

我也是從TOM大叔的這些部落格中才瞭解到作用域鏈的,之前也看過了許多本書籍,都沒有很清晰的展開作用域鏈這個概念。其實作用域鏈簡單說來也好理解,如下程式碼:

        var x = 10;
        function fn() {
            var y = 20;
            return function () {
                var z = 30
                console.log(x + y + z);
            }
        }

上面程式碼中,如果想要列印 x+y+z 的值,就必須要遍歷三個層次的上下文環境或者作用域,這其實和原型鏈的結構表現形式類似。但要細細將來,連同閉包圖文並茂的說明白,需要很多內容。

此處不再深入進去,以後有機會再另起一篇詳細介紹。

3. 二維鏈查詢 

上文講到通過作用域練向上查詢變數,實際在查詢變數的過程中,是使用“二維鏈查詢”——“作用域鏈” + “原型鏈”。看如下程式碼:

       Object.prototype.x = 10;
        function fn() {
            var y = 20;
            return function () {
                var z = 30
                console.log(x + y + z);
            }
        }

這份程式碼跟上文中演示作用域鏈的程式碼差不多,但是它卻通過 Object.prototype.x = 10; 這麼一句話,表現出了原型鏈在其中的作用。
因此,在查詢變數值時,是同時兼顧原型鏈和作用域鏈兩個方向的,即“二維鏈查詢”。 

4. 獨立作用域只能通過函式來建立

這句話的下半句是——不能通過if/for等語句塊來建立。後半句大家可能知道,但是它的本質確實前半句——獨立作用域只能通過函式來建立(除了獨立作用域之外,剩下的就是全域性作用域)。既然獨立作用域只能通過函式來建立,那麼函式中任何地方的自由變數就都是函式層級的,因此,以下程式碼希望不要再次出現:

5. 隱式全域性變數的本質

var a = 10;
b = 20;

以上兩句程式碼,看似都是宣告兩個全域性變數,但是按照TOM大叔說的,只有var才能宣告一個變數,也就是 var a = 10; 是真正的宣告變數。

而下一句 b = 20,其實是相當於設定window的一個屬性值而已。

因此,第一句的本質是宣告一個全域性變數;第二句的本質是設定window的一個屬性值。

當然,不推薦用第二句的形式。

6. 函式宣告和函式表示式的不同

js定義函式的方法有多種,但看看以下這段程式碼:

fn();
var fn = function() {  //函式表示式
    alert(123); // 報錯
}
//------
fn();
function fn() {  //函式宣告
    alert(123); // 123
}

兩種函式定義方式,卻得出不一樣的結果。

此處我當時沒有詳細看,因為這樣使用的情況不是很多,所以就沒有過深入的細看,只是做了個標記。如果有了解的朋友,不放解釋一下。

7.js使用靜態作用域

在part1中講過,當一個函式作為引數被傳入,後者作為一個值被返回的時候,連同它一塊被傳遞的,是它的作用域。也就是我們們常說的閉包。且看如下程式碼:

var x = 10;            
function foo() {
    alert(x);
}
(function (funarg) {
    var x = 20;
    funarg();    // 10, 不是20
})(foo); 

foo是一個函式,把它作為引數傳入進另一個函式中執行,連同一起傳遞的,是foo的作用域。而foo使用的是靜態作用域,其中的變數x在傳遞的時候已經被靜態賦值,不會受其他環境下x變數的影響。
這個道理也同樣適用於函式作為返回值。如下:

function fn() {
    var x = 10;
    return function () {
        alert(x);
    }
}
var ret = fn();
var x = 20;
ret();   // 10,不是20

 

更多內容請關注我的微博

 

相關文章