1. 程式人生 > >JS 通過沿著作用域鏈還是原型鏈查詢變數

JS 通過沿著作用域鏈還是原型鏈查詢變數

這是一道非常典型的JS閉包問題,結果和具體的解析請看這裡

對於其中的`函式作用域鏈的問題`博主似乎沒有解釋清楚,有一些疑問:js中的變數到底是沿著作用域鏈還是原型鏈查詢呢?

首先,要分清作用域鏈與原型鏈的區別,簡單來說

作用域鏈是相對於函式的,原型鏈是相對於物件的

js中訪問變數有多種方式
1. 直接通過識別符號訪問
2. 通過 . 或 [] 訪問物件中的識別符號

猜想訪問方式不同,導致了查詢的方式不同:
1. 直接通過識別符號訪問,訪問的可能是函式中的識別符號,也可能是全域性物件(瀏覽器中是 window)的識別符號,也就是說,可能沿著作用域鏈也可能沿著原型鏈訪問
2. 通過 . 或 [] 訪問物件中的識別符號,js會沿著原型鏈查詢

對於第二點,以下的小測試可以證明

① 相當於呼叫 window.test(),this 指向 window,訪問的是 window.a;

② 中 this 指向 o,訪問的是 o.a;

將全域性的 a, 也就是 window.a 和 o.a 刪除之後,得到的結果均是 1。

因此,通過 . 或 [] 訪問`物件`中的識別符號,js會沿著原型鏈查詢。

第一點,直接通過識別符號訪問,也就是訪問當前執行上下文EC的作用域中的變數,這一過程稱為識別符號解析,依賴於作用域鏈。
作用域鏈Scope其實就是對執行上下文EC中的變數物件VO|AO有序訪問的連結串列

關於作用域鏈與執行上下文 EC 的關係,請看這裡

測試

把 this.a 改為 a,②的結果就變啦。

① 和 ② 的執行上下文EC(即 this 的指向)分別為 window 和 o,但作用域鏈都是 test變數物件 + 全域性變數物件。而test變數物件中沒有 a,全域性變數物件含有 a 。這就說明,在函式中直接通過識別符號變數,js會沿著作用域中查詢。

有趣的是,當刪除了全域性變數物件中的 a,再訪問 a,瀏覽器並沒有報錯,而是輸出 4;刪除 Window.prototype.a 之後,輸出的這是 1.
在 test 中新增一下程式碼 :

由結果可知,訪問到的a 分別為 Window.prototype 和 Object.prototype 中的變數。

為什麼呢?因為在瀏覽器中,全域性變數物件在瀏覽器中指向 window, window 也是物件,且位於作用域鏈的末尾;作用域鏈查詢完,仍然找不到,js 就會沿著全域性變數物件的原型鏈查詢。

結論

1. 直接通過識別符號訪問變數,首先沿著作用域鏈查詢每一個變數物件,直到全域性變數物件(window)仍沒有,就沿著全域性變數物件(window)的原型鏈查詢
2. 通過 . 或 [] 訪問物件中的識別符號,就直接沿著原型鏈查詢