深入理解javascript原型和閉包(14)——從【自由變量】到【作用域鏈】
先解釋一下什么是“自由變量”。
在A作用域中使用的變量x,卻沒(méi)有在A作用域中聲明(即在其他作用域中聲明的),對(duì)于A作用域來(lái)說(shuō),x就是一個(gè)自由變量。如下圖

如上程序中,在調(diào)用fn()函數(shù)時(shí),函數(shù)體中第6行。取b的值就直接可以在fn作用域中取,因?yàn)閎就是在這里定義的。而取x的值時(shí),就需要到另一個(gè)作用域中取。到哪個(gè)作用域中取呢?
有人說(shuō)過(guò)要到父作用域中取,其實(shí)有時(shí)候這種解釋會(huì)產(chǎn)生歧義。例如:

所以,不要在用以上說(shuō)法了。相比而言,用這句話描述會(huì)更加貼切——要到創(chuàng)建這個(gè)函數(shù)的那個(gè)作用域中取值——是“創(chuàng)建”,而不是“調(diào)用”,切記切記——其實(shí)這就是所謂的“靜態(tài)作用域”。
對(duì)于本文第一段代碼,在fn函數(shù)中,取自由變量x的值時(shí),要到哪個(gè)作用域中取?——要到創(chuàng)建fn函數(shù)的那個(gè)作用域中取——無(wú)論fn函數(shù)將在哪里調(diào)用。
上面描述的只是跨一步作用域去尋找。
如果跨了一步,還沒(méi)找到呢?——接著跨!——一直跨到全局作用域?yàn)橹埂R窃谌肿饔糜蛑卸紱](méi)有找到,那就是真的沒(méi)有了。
這個(gè)一步一步“跨”的路線,我們稱(chēng)之為——作用域鏈。
我們拿文字總結(jié)一下取自由變量時(shí)的這個(gè)“作用域鏈”過(guò)程:(假設(shè)a是自由量)
第一步,現(xiàn)在當(dāng)前作用域查找a,如果有則獲取并結(jié)束。如果沒(méi)有則繼續(xù);
第二步,如果當(dāng)前作用域是全局作用域,則證明a未定義,結(jié)束;否則繼續(xù);
第三步,(不是全局作用域,那就是函數(shù)作用域)將創(chuàng)建該函數(shù)的作用域作為當(dāng)前作用域;
第四步,跳轉(zhuǎn)到第一步。

以上代碼中:第13行,fn()返回的是bar函數(shù),賦值給x。執(zhí)行x(),即執(zhí)行bar函數(shù)代碼。取b的值時(shí),直接在fn作用域取出。取a的值時(shí),試圖在fn作用域取,但是取不到,只能轉(zhuǎn)向創(chuàng)建fn的那個(gè)作用域中去查找,結(jié)果找到了。
這一節(jié)看似很輕松的把作用域鏈引出來(lái),并講完了。之所有輕松是有前幾節(jié)的基礎(chǔ),否則將很難解釋。
接下來(lái)咱們開(kāi)始正式說(shuō)說(shuō)一直期待依舊的朋友——閉包。敬請(qǐng)期待下一節(jié)。
---------------------------------------------------------------------------
本文已更新到《深入理解javascript原型和閉包系列》的目錄,更多內(nèi)容可參見(jiàn)《深入理解javascript原型和閉包系列》。
另外,歡迎關(guān)注我的微博。
學(xué)習(xí)作者教程:《前端JS高級(jí)面試》《前端JS基礎(chǔ)面試題》《React.js模擬大眾點(diǎn)評(píng)webapp》《zepto設(shè)計(jì)與源碼分析》《json2.js源碼解讀》

浙公網(wǎng)安備 33010602011771號(hào)