天天躁日日躁狠狠躁AV麻豆-天天躁人人躁人人躁狂躁-天天澡夜夜澡人人澡-天天影视香色欲综合网-国产成人女人在线视频观看-国产成人女人视频在线观看

javascript 必知必會之closure

下面的代碼片斷縮進目前還不完善,你也可以選擇 下載pdf 來閱讀.

Contents

  • 摘要
  • 什么是closure
  • 執(zhí)行空間(執(zhí)行上下文, Execution Context)
  • closure的一些用法
  • 關于closure的效率
  • 應用建議
  • 結論
  • 參考資料
  • 本文的rst源碼

什么是closure

一種定義是:

A "closure" is an expression (typically a function) that can have free variables together with an environment that binds those variables (that "closes" the expression).

我的理解是: closure 是一個表達式(通常是一個函數(shù)), 這個表達式與一個 環(huán)境 共享著一些自由變量, 而這個 環(huán)境 則 綁定 著那些自由變量(或者說 結束 這個表達式, 這也是所謂closure 的名字由來). 所謂的 環(huán)境 就是一個更大的block, 所有的自由變量在這個 block 中 聲明(有意義). 而 綁定 也就是指這些自由變量的作用域就是這個環(huán)境.

舉個簡單的例子.

var flag = false; //調試開關 // env 既是所謂的環(huán)境 // 而inner就是所謂的表達式, name即是所謂的自由變量 function env() //整個env可以看作是一個closure { var name = "zhutao"; function inner() { return name + " is a student."; } return inner; //返回的是一個內部函數(shù) }//closure結束 flag = true; if (flag) { // 此處是最神奇的地方, 代碼執(zhí)行在此處, inner函數(shù)其實已經(jīng)出了env的body, // 而仍然能夠被引用, 這就是所謂形成了一個 closure var inner_func_ref = env(); // 這時候inner_func_ref引用的就是inner()函數(shù)對象 alert(inner_func_ref()); // zhutao is a student. } 

而在上面的例子中, 函數(shù)env就是所謂的定義中的 環(huán)境, 函數(shù)inner就是定義中所謂的 表達式, 而name即是所謂的 自由變量綁定 在env這個 環(huán)境 中. env的結束也即closure的結束.

而在Javascript中,如果內部函數(shù)出了自己的所在的外部函數(shù)的body仍然能夠引用,則會形成所謂的closure.

在具體了解closure之前,我們需要了解一些其它的知識.

執(zhí)行空間(執(zhí)行上下文, Execution Context)

在 Javascript 中,每行可執(zhí)行的代碼都具有一定的 執(zhí)行空間, 如全局的執(zhí)行空間, 函數(shù)的執(zhí)行空間, 遞歸后的函數(shù)執(zhí)行空間等. 而一個完整的 Javascript 執(zhí)行過程,可以看作是有一個執(zhí)行空間棧 ,不斷地 進行 執(zhí)行空間 的變化(出棧,進棧).

這個是很重要的概念,這個概念的理解與本系列的將要完成的另一篇文章 this關鍵字 的理解也是密切相關的.

詳細解釋請參考即將完成的 this關鍵字 的博文.

執(zhí)行空間可以理解為具有屬性的對象集, 但是通常這些屬性都不是可隨意訪問的, 而這些對象集為代碼的執(zhí)行 提供了一定的上下文(空間).

當執(zhí)行到一個函數(shù)時, 會建立此函數(shù)的執(zhí)行空間(所謂進棧), 執(zhí)行結束了, 從此執(zhí)行空間退出返回到原來的執(zhí)行空間(所謂 的出棧),而js解釋器在運行過程中一起維護著這樣一個 執(zhí)行空間棧 來為不同的代碼提供不同的執(zhí)行空間.

那么執(zhí)行空間與closure有什么關系?

簡單地說,一定的執(zhí)行空間對應著一定的closure, 只有位于同一個closure的方法才能訪問同一closure的變量.

舉個簡單的例子:

// 關于context的例子 flag = true; var tmpobj = { name : "zhutao", func : function(){ return "call by func " + this.name; } }; if (flag) { // 代碼執(zhí)行在此處時context還是global alert(tmpobj.name); alert(tmpobj.func()); //進入func的context // 回到global的context } 

closure的一些用法

當內部函數(shù)和自由變量位于同一closure時,可以隨意訪問,而聲明順序并不重要.

幾個常用的例子:

//一些應用 flag = true; function OuterFun() { var num = 100; var printNum = function(){alert(num);} //此處引用的num是引用,而不是值,所以后面改變num,此處的num同樣生效 num ++; return printNum; } var myfunc = OuterFun(); myfunc(); //輸出的是101,而不是100 //另一個例子,下面的例子,可以看到匿名函數(shù)(內部函數(shù))先于外部函數(shù)變量的聲明,但是仍然能夠訪問外部函數(shù)的變量 // 也就是說內部函數(shù)與外部函數(shù)的變量位于同一個closure, 所以可以訪問 function SameClosure() { var iCanAccess = function(){alert(name);}; var name = "zhutao"; return iCanAccess; } var testSameClosure = SameClosure(); testSameClosure();// zhutao // 另一個應用,關于module pattern, 這樣可以實際所謂的 private, public等方法和變量 var module = (function Module(){ var privateVar = "zhutao is private"; // private return { publicGetPrivateVar : function(){ return privateVar; }, // public method, 可以取所謂的private變量 publicVar : "I'm a public variable" // public variable }; })(); if (flag) { alert(module.publicGetPrivateVar()); // zhutao is private alert(module.publicVar); // I'm a public variable alert(module.privateVar); // undefined } 

關于closure的效率

因為在closure的實際應用可能會多次去生成一個內部函數(shù)(匿名),所以存在可能的效率問題.(對象的建立,內存管理釋放等).

所以,應該盡量減少內部函數(shù)的生成, 而使用函數(shù)的引用.

例如:

// 關于效率的例子 flag = false; // 這樣,每次調用Outer時會產(chǎn)生匿名函數(shù)的開銷 function Outer(obj) { obj.fun = function(){ alert("I am " + this.name); }; } if (flag) { var obj = { name : "zhutao"}; Outer(obj); obj.fun(); } // 更好的處理方式 function Outer_better(obj) { obj.fun = showme; // 這樣調用的只是函數(shù)的引用 } function showme() { alert("I am " + this.name); } if (flag) { var obj2 = { name : "zhutao"}; Outer_better(obj2); obj2.fun(); } 

應用建議

Don't use closures unless you really need closure semantics. In most cases, nonnested functions are the right way to go. Eric Lippert, Microsoft 

上面的論述是基于效率的考慮, 而 IE 4-6 在使用closure時可能會存在內存泄露的問題,參考 JavaScript Closures 中的相關部分.

而在某些場合,你可能必須要使用closure, 如 循環(huán)問題.

代碼:

flag = true; // 向body中生成一些鏈接,然后綁定事件 function addLink(num) { for(var i=0; i<num; i++) { var link = document.createElement('a'); link.innerHTML = "Link " + i; link.onclick = function(){ alert(i); }; document.body.appendChild(link); } } //可惜的是,當你點擊每個鏈接時,輸出的都是 Link 4 // 使用closure 可以解決這個問題 function addLink2(num) { for(var i=0; i<num; i++) { var link = document.createElement('a'); link.innerHTML = "Link" + i; link.onclick = function(j){ //使用closure return function(){ alert(j); };//返回一個函數(shù) }(i);//調用這個函數(shù) document.body.appendChild(link); } } window.onload = addLink(4); window.onload = addLink2(4); 

為什么會出現(xiàn)上面的這個問題?(事實在之前的的一個項目中,也遇到了相同的問題,但是當時還不懂closure, 也是一頭霧水)

這是因為,對于addLink, 在退出addLink函數(shù)之前, i已經(jīng)變成了4,所以無論后面的事件觸發(fā),輸出的都是4.

但是后者,使用了closure.使得j引用了當前的循環(huán)中的i,所以對于每個后續(xù)觸發(fā)事件,都會按照預期地得到相應的結果.

具體的討論可見: SO

這即是一個典型的closure應用場景, 而如果不使用, 就無法解決這個問題.

結論

下面這段摘抄自 Summary of JavaScript closures :

  1. 當你在一個函數(shù)中使用另一個函數(shù)時, 會產(chǎn)生一個closure
  2. 當你使用eval()時, 會產(chǎn)生一個closure.
  3. 最好認為closure總是在函數(shù)入口處產(chǎn)生,并且本地變量自動添加到closure中

其它的細節(jié)可參考上面的鏈接.

總之, 關于closure,你必須記住以下幾點:

  1. closure就是提供了一種變量共享的機制(內部函數(shù)可以訪問外部函數(shù)的變量)
  2. 注意closure可能引用的效率問題(如何避免,參見文中詳述)
  3. 具體的應用場景要熟悉

上篇博文講的是 prototype, 下篇博文預計會講 this關鍵字, 歡迎大家討論和留言.

參考資料

  1. JavaScript Closures
  2. Explaining JavaScript Scope And Closures
  3. JavaScript Closures 101
  4. JavaScript and memory leaks
  5. Closures in JavaScript

本文的rst源碼

本文的源碼鏈接在 這里 .

本文中涉及的Javascript代碼可以在 這兒 下載.

你也可以選擇 下載pdf 來閱讀.

JavaScript技術javascript 必知必會之closure,轉載需保留來源!

鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播更多信息之目的,如作者信息標記有誤,請第一時間聯(lián)系我們修改或刪除,多謝。

主站蜘蛛池模板: 美女搜查官被高难度黑人在线播放 | 亚洲蜜芽在线观看精品一区 | 国精产品一区一区三区有限 | 精品国产乱码久久久久久口爆 | 国产午夜一区二区三区免费视频 | 真实的强视频免费网站 | 蜜柚免费视频高清观看在线 | 欧美 亚洲综合在线一区 | 亚洲中文字幕日本在线观看 | 国产品无码一区二区三区在线 | 久久99这里只有精品 | 老王午夜69精品影院 | 欧美特级特黄AAAAA片 | 99久酒店在线精品2019 | WWW国产亚洲精品久久久日本 | 伸进同桌奶罩里摸她胸作文 | 试看做受120秒免费午夜剧场 | 青柠在线电影高清免费观看 | 观看免费做视频 | 亚洲欧美日本国产在线观18 | 久久毛片网站 | 亚洲三级视频在线 | 99国产精品偷窥熟女精品视频 | 帝王被大臣们调教高肉 | 成人片免费看 | 欧美激情视频一区 | 亚洲免费每日在线观看 | 国产精品久久毛片A片软件爽爽 | 色婷婷激婷婷深爱五月小蛇 | 中文无码不卡的岛国片国产片 | 优菈的乳液狂飙天堂W98 | 亚州精品视频 | 在线播放一区二区精品产 | 国产亚洲精品久久久久小 | 国产成人精品精品欧美 | 手机在线观看毛片 | 又色又爽又黄gif动态视频 | 国产成人在线视频观看 | 午夜在线视频国产极品片 | 总裁呻吟双腿大开男男H | 国产精品人妻无码免费A片导航 |