阮一峯的一篇文章已經對閉包的用途、概念講解地相對清晰了。javascript
閉包就是可以讀取其餘函數內部變量的函數。html
但我認爲裏面對於做用域鏈的解釋還不夠清晰,這裏做一些補充。java
閉包之因此能夠讀取外部函數的內部變量,即便外部函數已經返回,是由於它把外部函數的活動對象加到了本身的做用域鏈中。閉包
而理解做用域對於徹底理解閉包很重要。函數
對於這段代碼測試
function compare(value1,value2){ alert("this "+this+" arguments "+arguments); if(value1 < value2){ return -1; }else if( value1 > value2){ return 1; }else{ return 0; } } var result = compare(5,10);
JavaScript高級程序設計裏這樣寫this
在建立函數compare()時,會建立一個預先包含全局變量的做用域鏈,當調用時,會爲函數建立一個執行環境。spa
其做用域鏈中包含兩個變量對象:本地活動對象和全局變量對象。設計
做用域鏈本質上是一個指向變量對象的指針列表。3d
畫出來大概是這個樣子的
舉個閉包的例子
var name = "the window"; var object = { name: "My Object", getNameFunc: function(){ return function () { return this.name; } } }
不少人不明白爲何this.name不指向包含函數中的object.name。
對於這個問題,一樣是這本書中有一句話
每一個函數被調用時都會自動取得兩個特殊變量:this 和 arguments。內部函數在搜索這兩個變量時,只會搜索到其活動對象爲止。
意思是,匿名函數的this是按做用域鏈搜索的,當在匿名函數自己的活動對象中就找到this後,就不會再向外搜索了。this指向的活動對象中並無name屬性,因此指向了全局的this。
做用域鏈畫出來是這樣的
如今明白了吧,閉包之因此有這樣的表現,是由於搜索標識符時按照做用域鏈,從內向外搜索。
再看下這個例子你會更清楚。
var name = "the window"; var object = { name: "My Object", getNameFunc: function(){ var that = this; return function () { alert("that "+that); return that.name; } } }
變化在於這裏
匿名函數在自身的活動對象裏沒找到that對象,就沿做用域鏈向上找,在getNameFunc()的活動對象裏找到了that,指向的就是this。
因此你能夠按F12本身測試一下,輸出結果是"My Object"