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
更多內容請關注我的微博