這篇文章,來繼續(xù)談?wù)凧avascript閉包的剩余問題。因?yàn)樵谏弦黄恼轮?a href="http://www.itdecent.cn/p/794812620271" target="_blank">關(guān)于JS閉包(JS閉包系列1)主要簡(jiǎn)單的示例代碼直觀的觀察了下閉包,這篇文章就來從理論上好好地分析分析JS閉包的有關(guān)概念。
首先應(yīng)該知道的是JS中的作用域鏈(scope chain)概念:
(1)定義
每個(gè)人都有自己的生活環(huán)境,代碼也一樣,JS中的代碼都有自己的“執(zhí)行環(huán)境”。當(dāng)代碼在一個(gè)環(huán)境中定義時(shí)(函數(shù)調(diào)用之前),就會(huì)生成變量對(duì)象的作用域鏈。這個(gè)鏈條,從當(dāng)前環(huán)境中的變量對(duì)象開始,上溯至父環(huán)境(包含環(huán)境)中的變量對(duì)象,再到父環(huán)境的父環(huán)境中的變量對(duì)象···一直上溯到全局執(zhí)行環(huán)境的變量對(duì)象。想象著一條鎖鏈,從最下面開始爬,一直爬到最上頂端。每一個(gè)節(jié)點(diǎn),代表著這一層級(jí)環(huán)境中的變量對(duì)象。
需要注意的是,作用域鏈?zhǔn)歉鶕?jù) 函數(shù)定義 時(shí)的位置確定的,而不是在調(diào)用時(shí)確定。這也叫做 詞法作用域 。

(2)作用
作用域鏈用來保證那些有權(quán)訪問不同執(zhí)行環(huán)境的代碼,在對(duì)不同環(huán)境里的變量和函數(shù)訪問時(shí)的有序性以及確定性。
(3)應(yīng)用
當(dāng)解析一個(gè)標(biāo)識(shí)符時(shí),是沿著作用域鏈一級(jí)一級(jí)地上溯(回溯)搜索標(biāo)識(shí)符的過程。如果找不到標(biāo)識(shí)符,就會(huì)產(chǎn)生錯(cuò)誤。
還是直接看代碼吧:
var color = "blue"; //全局作用域
function changeColor(){
if(color === "blue"){ //尋找`color`,此作用域(函數(shù)內(nèi))沒有,則上溯到父級(jí)作用域,(這里是全局)找到了
color = "red";
}else{
color = "blue";
}
}
changeColor();
alert("Color is now " + color);
在這個(gè)例子中,函數(shù)changeColor的作用域鏈包含兩個(gè)對(duì)象:它自己的變量對(duì)象(每個(gè)函數(shù)都有的arguments對(duì)象),以及全局環(huán)境中的變量對(duì)象。
再來看一個(gè)多層作用域鏈?zhǔn)纠?/p>
var weibing = "在"; //最外層守衛(wèi)
var dajiangjun = "不在"; //2號(hào)大將軍
function chengqiang(){ //城墻內(nèi)
var dajiangjun= "在"; //1號(hào)大將軍
function gongdian(){ //宮殿內(nèi)
var zaixiang = "不在"; //宰相
if(zaixiang === "在")
console.log("把宰相找來!");
else if(dajiangjun === "在")
console.log("把大將軍找來!");
else
console.log("把衛(wèi)兵找來!");
}
return gongdian();
}
chengqiang();
結(jié)果可想而知:

想象這樣一個(gè)場(chǎng)景:皇帝身居寢宮,寂寞了,想要招納美女。那么首先想到的是宰相,他可以直接召見宰相,讓宰相給自己辦事。假如宰相不在,那么他可以召見大將軍(職級(jí)低于宰相),讓大將軍給自己干;碰巧,大將軍也去喝酒了,那就直接召見衛(wèi)兵。在這個(gè)例子中,皇帝找到大將軍了。但是有人會(huì)問,這段代碼里,有兩個(gè)dajiangjun啊!對(duì)啊,可是,皇帝他老人家是從自己身邊找人的。最外邊的那個(gè)2號(hào)大將軍,雖然也在,但是他沒上朝,去考察去了。(沒在城墻內(nèi)),所以就召見1號(hào)大將軍嘍。而且自然而然,在城墻外邊的人(例如普通老百姓),當(dāng)然是見不到宰相和皇帝的啦。
例如在上面代碼下面加如下代碼:
function chixinwangxiang(){ //癡心妄想
alert("把"+zaixiang+"給爺叫出來!");
}
結(jié)果是:

在城墻外邊,你肯定是見不到一人之下,萬人之上的宰相大人啦!
(4)注意
因?yàn)樵贘S中,作用域里沒有以花括號(hào)包圍的“塊級(jí)作用域”概念,看一個(gè)例子:
if(true){
var a = 1;
}
console.log(a); //輸出1
表面上看來, a在if語句里面定義著,那么console語句應(yīng)該是訪問不了的,然而事實(shí)恰恰相反,因?yàn)樵贘S中,if或者for語句的花括號(hào),根本不是獨(dú)立作用域,只有函數(shù)的花括號(hào)才起作用。
總結(jié):現(xiàn)在知道了作用域鏈的概念,而你應(yīng)該知道的是,閉包就是由作用域鏈引起的。下一節(jié),就要帶著作用域鏈的思維來思考閉包,那樣就簡(jiǎn)單多了。因?yàn)槟銜?huì)發(fā)現(xiàn)那是順其自然。