一次性搞懂JavaScript閉包

引言:JavaScript中的閉包和離散數(shù)學中的閉包并沒有任何關系渊抄。
The use of the word closure here comes from abstract algebra, where a set of elements is said to be closed under an operation if applying the operation to elements in the set produces an element that is again an element of the set. The Lisp community also (unfortunately) uses the word closure to describe a totally unrelated concept.

作用域

簡單的說垫卤,作用域就是變量與函數(shù)的可訪問范圍牲阁,即作用域控制著變量與函數(shù)的可見性和生命周期癌蓖。在JavaScript中瑟押,變量的作用域有全局作用域和局部作用域兩種。

全局作用域
var num1 = 1;
function fun1 (){
    num2 = 2;
}

以上三個對象num1,num2fun1均是全局作用域戳杀,這里要注意的是末定義直接賦值的變量自動聲明為擁有全局作用域疚漆;

局部作用域
function wrap(){
    var obj = "我被wrap包裹起來了,wrap外部無法直接訪問到我";
    function innerFun(){
        //外部無法訪問我
    }
}

作用域鏈

Javascript中一切皆對象熔吗,這些對象有一個[[Scope]]屬性辆床,該屬性包含了函數(shù)被創(chuàng)建的作用域中對象的集合,這個集合被稱為函數(shù)的作用域鏈(Scope Chain)桅狠,它決定了哪些數(shù)據(jù)能被函數(shù)訪問讼载。

function add(a,b){
    return a+b;
}

當函數(shù)創(chuàng)建的時候,它的[[scope]]屬性自動添加好全局作用域

函數(shù)創(chuàng)建時的作用域鏈

var sum = add(3,4);

當函數(shù)調(diào)用的時候中跌,會創(chuàng)建一個稱為運行期上下文(execution context)的內(nèi)部對象咨堤,z這個對象定義了函數(shù)執(zhí)行時的環(huán)境。它也有自己的作用域鏈漩符,用于標識符解析一喘,而它的作用域鏈初始化為當前運行函數(shù)的[[Scope]]所包含的對象。

函數(shù)執(zhí)行時

在函數(shù)執(zhí)行過程中,每遇到一個變量凸克,都會經(jīng)歷一次標識符解析過程以決定從哪里獲取和存儲數(shù)據(jù)议蟆。該過程從作用域鏈頭部,也就是從活動對象開始搜索萎战,查找同名的標識符咐容,如果找到了就使用這個標識符對應的變量,如果沒找到繼續(xù)搜索作用域鏈中的下一個對象蚂维,如果搜索完所有對象(最后一個為全局對象)都未找到戳粒,則認為該標識符未定義。

閉包

閉包簡單來說就是一個函數(shù)訪問了它的外部變量虫啥。

var quo = function(status){
    return {
        getStatus: function(){
            return status;
        }
    }
}

status保存在quo中蔚约,它返回了一個對象,這個對象里的方法getStatus引用了這個status變量涂籽,即getStatus函數(shù)訪問它的外部變量status苹祟;

var newValue = quo('string');//返回了一個匿名對象,被newValue引用著
newValue.getStatus();//訪問到了quo的內(nèi)部變量status

假如并沒有getStatus這個方法评雌,那么quo('sting')結束后苔咪,status自動被回收,正是因為返回的匿名對象被一個全局對象引用柳骄,那么這個匿名對象又依賴于status,所以會阻止status的釋放箕般。

例子
//錯誤方案
var test = function(nodes){
    var i ;
    for(i = 0;i<nodes.length;i++){
        nodes[i].onclick = function(e){
            alert(i);
        }
    }
}

匿名函數(shù)創(chuàng)建了一個閉包耐薯,那么其訪問的i是外部test函數(shù)中的i,所以每一個節(jié)點實際上引用的是同一個i丝里。

錯誤的結果
//改進方案
var test = function(nodes){
    var i ;
    for(i = 0;i<nodes.length;i++){
        nodes[i].onclick = function(i){
            return function(){
                alert(i);
            };
        }(i);
    }
}

每一個節(jié)點綁定了一個事件曲初,這個事件接收一個參數(shù),并且立即運行杯聚,傳入i臼婆,因為是按值傳遞的,所以每一次循環(huán)都會為當前i產(chǎn)生一個新的備份幌绍。

正確使用后的測試

思考題目

轉載自阮一峰博客颁褂,出自《JavaScript高級程序設計》

題目一
 var name = "The Window";
  var object = {
    name : "My Object",
    getNameFunc : function(){
      return function(){
        return this.name;
      };
    }
  };
  alert(object.getNameFunc()());

運行結果:The Window

解釋:object.getNameFunc()這是屬于方法調(diào)用,this指針指向的是object傀广,可以用一個變量tmp引用它的結果颁独,實際上tmp就是這個方法返回的那個匿名函數(shù)function(){return this.name;};,此時并沒有執(zhí)行內(nèi)部代碼伪冰,執(zhí)行tmp()時誓酒,也就是object.getNameFunc()()時,屬于函數(shù)調(diào)用(另一篇博文詳解了這里贮聂,鏈接)靠柑,this指針指向window寨辩,最終返回The Window

題目二
var name = "The Window";
  var object = {
    name : "My Object",
    getNameFunc : function(){
      var that = this;
      return function(){
        return that.name;
      };
    }
  };
  alert(object.getNameFunc()());

運行結果:My Object

解釋:在調(diào)用getNameFunc()時歼冰,屬于方法調(diào)用靡狞,那么this指針指向object,把它被that引用停巷,那么返回的匿名函數(shù)中時刻保持對object的引用耍攘,很好理解。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末畔勤,一起剝皮案震驚了整個濱河市蕾各,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌庆揪,老刑警劉巖式曲,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異缸榛,居然都是意外死亡吝羞,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門内颗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來钧排,“玉大人,你說我怎么就攤上這事均澳『蘖铮” “怎么了?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵找前,是天一觀的道長糟袁。 經(jīng)常有香客問我,道長躺盛,這世上最難降的妖魔是什么项戴? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮槽惫,結果婚禮上周叮,老公的妹妹穿的比我還像新娘。我一直安慰自己躯枢,他們只是感情好则吟,可當我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著锄蹂,像睡著了一般氓仲。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天敬扛,我揣著相機與錄音晰洒,去河邊找鬼。 笑死啥箭,一個胖子當著我的面吹牛谍珊,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播急侥,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼砌滞,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了坏怪?” 一聲冷哼從身側響起贝润,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎铝宵,沒想到半個月后打掘,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡鹏秋,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年尊蚁,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片侣夷。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡横朋,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出百拓,到底是詐尸還是另有隱情叶撒,我是刑警寧澤,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布耐版,位于F島的核電站,受9級特大地震影響压汪,放射性物質(zhì)發(fā)生泄漏粪牲。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一止剖、第九天 我趴在偏房一處隱蔽的房頂上張望腺阳。 院中可真熱鬧,春花似錦穿香、人聲如沸亭引。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽焙蚓。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間购公,已是汗流浹背萌京。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留宏浩,地道東北人知残。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像比庄,于是被迫代替她去往敵國和親求妹。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,925評論 2 344

推薦閱讀更多精彩內(nèi)容

  • 作用域和閉包是 JavaScript 最重要的概念之一佳窑,想要進一步學習 JavaScript制恍,就必須理解 Java...
    劼哥stone閱讀 1,170評論 1 13
  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn)华嘹,斷路器吧趣,智...
    卡卡羅2017閱讀 134,601評論 18 139
  • 介紹 JavaScript中有一個被稱為作用域(Scope)的特性。雖然對于許多新手開發(fā)者來說耙厚,作用域的概念并不是...
    安_6dd1閱讀 958評論 0 8
  • 閉包沒有想象的那么簡單 閉包的概念在JavaScript中占據(jù)了十分重要的地位强挫,有不少開發(fā)者分不清匿名函數(shù)和閉包的...
    老馬的春天閱讀 724評論 2 8
  • ?3.天使??鼓勵,22李孝敏薛躬,今天夸夸你的夫妻關系俯渤。那天笑來?直播課上,李老師再次強調(diào)了要把稀缺的注意力放到親密...
    Grit888閱讀 102評論 0 0