先解釋一下什麼是「自由變量」。javascript
在A做用域中使用的變量x,卻沒有在A做用域中聲明(即在其餘做用域中聲明的),對於A做用域來講,x就是一個自由變量。以下圖html
如上程序中,在調用fn()函數時,函數體中第6行。取b的值就直接能夠在fn做用域中取,由於b就是在這裏定義的。而取x的值時,就須要到另外一個做用域中取。到哪一個做用域中取呢?前端
有人說過要到父做用域中取,其實有時候這種解釋會產生歧義。例如:java
因此,不要在用以上說法了。相比而言,用這句話描述會更加貼切——要到建立這個函數的那個做用域中取值——是「建立」,而不是「調用」,切記切記——其實這就是所謂的「靜態做用域」。web
對於本文第一段代碼,在fn函數中,取自由變量x的值時,要到哪一個做用域中取?——要到建立fn函數的那個做用域中取——不管fn函數將在哪裏調用。面試
上面描述的只是跨一步做用域去尋找。json
若是跨了一步,還沒找到呢?——接着跨!——一直跨到全局做用域爲止。要是在全局做用域中都沒有找到,那就是真的沒有了。閉包
這個一步一步「跨」的路線,咱們稱之爲——做用域鏈。app
咱們拿文字總結一下取自由變量時的這個「做用域鏈」過程:(假設a是自由量)webapp
第一步,如今當前做用域查找a,若是有則獲取並結束。若是沒有則繼續;
第二步,若是當前做用域是全局做用域,則證實a未定義,結束;不然繼續;
第三步,(不是全局做用域,那就是函數做用域)將建立該函數的做用域做爲當前做用域;
第四步,跳轉到第一步。
以上代碼中:第13行,fn()返回的是bar函數,賦值給x。執行x(),即執行bar函數代碼。取b的值時,直接在fn做用域取出。取a的值時,試圖在fn做用域取,可是取不到,只能轉向建立fn的那個做用域中去查找,結果找到了。
這一節看似很輕鬆的把做用域鏈引出來,並講完了。之全部輕鬆是有前幾節的基礎,不然將很難解釋。
接下來我們開始正式說說一直期待依舊的朋友——閉包。敬請期待下一節。
---------------------------------------------------------------------------
本文已更新到《深刻理解javascript原型和閉包系列》的目錄,更多內容可參見《深刻理解javascript原型和閉包系列》。
另外,歡迎關注個人微博。
學習做者教程:《前端JS高級面試》《前端JS基礎面試題》《React.js模擬大衆點評webapp》《zepto設計與源碼分析》《json2.js源碼解讀》