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

javascript 必知必會(huì)之closure

下面的代碼片斷縮進(jìn)目前還不完善,你也可以選擇 下載pdf 來(lái)閱讀.

Contents

  • 摘要
  • 什么是closure
  • 執(zhí)行空間(執(zhí)行上下文, Execution Context)
  • closure的一些用法
  • 關(guān)于closure的效率
  • 應(yīng)用建議
  • 結(jié)論
  • 參考資料
  • 本文的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 是一個(gè)表達(dá)式(通常是一個(gè)函數(shù)), 這個(gè)表達(dá)式與一個(gè) 環(huán)境 共享著一些自由變量, 而這個(gè) 環(huán)境 則 綁定 著那些自由變量(或者說(shuō) 結(jié)束 這個(gè)表達(dá)式, 這也是所謂closure 的名字由來(lái)). 所謂的 環(huán)境 就是一個(gè)更大的block, 所有的自由變量在這個(gè) block 中 聲明(有意義). 而 綁定 也就是指這些自由變量的作用域就是這個(gè)環(huán)境.

舉個(gè)簡(jiǎn)單的例子.

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

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

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

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

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

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

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

詳細(xì)解釋請(qǐng)參考即將完成的 this關(guān)鍵字 的博文.

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

當(dāng)執(zhí)行到一個(gè)函數(shù)時(shí), 會(huì)建立此函數(shù)的執(zhí)行空間(所謂進(jìn)棧), 執(zhí)行結(jié)束了, 從此執(zhí)行空間退出返回到原來(lái)的執(zhí)行空間(所謂 的出棧),而js解釋器在運(yùn)行過(guò)程中一起維護(hù)著這樣一個(gè) 執(zhí)行空間棧 來(lái)為不同的代碼提供不同的執(zhí)行空間.

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

簡(jiǎn)單地說(shuō),一定的執(zhí)行空間對(duì)應(yīng)著一定的closure, 只有位于同一個(gè)closure的方法才能訪問(wèn)同一closure的變量.

舉個(gè)簡(jiǎn)單的例子:

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

closure的一些用法

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

幾個(gè)常用的例子:

//一些應(yīng)用 flag = true; function OuterFun() { var num = 100; var printNum = function(){alert(num);} //此處引用的num是引用,而不是值,所以后面改變num,此處的num同樣生效 num ++; return printNum; } var myfunc = OuterFun(); myfunc(); //輸出的是101,而不是100 //另一個(gè)例子,下面的例子,可以看到匿名函數(shù)(內(nèi)部函數(shù))先于外部函數(shù)變量的聲明,但是仍然能夠訪問(wèn)外部函數(shù)的變量 // 也就是說(shuō)內(nèi)部函數(shù)與外部函數(shù)的變量位于同一個(gè)closure, 所以可以訪問(wèn) function SameClosure() { var iCanAccess = function(){alert(name);}; var name = "zhutao"; return iCanAccess; } var testSameClosure = SameClosure(); testSameClosure();// zhutao // 另一個(gè)應(yīng)用,關(guān)于module pattern, 這樣可以實(shí)際所謂的 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 } 

關(guān)于closure的效率

因?yàn)樵赾losure的實(shí)際應(yīng)用可能會(huì)多次去生成一個(gè)內(nèi)部函數(shù)(匿名),所以存在可能的效率問(wèn)題.(對(duì)象的建立,內(nèi)存管理釋放等).

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

例如:

// 關(guān)于效率的例子 flag = false; // 這樣,每次調(diào)用Outer時(shí)會(huì)產(chǎn)生匿名函數(shù)的開(kāi)銷 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; // 這樣調(diào)用的只是函數(shù)的引用 } function showme() { alert("I am " + this.name); } if (flag) { var obj2 = { name : "zhutao"}; Outer_better(obj2); obj2.fun(); } 

應(yīng)用建議

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時(shí)可能會(huì)存在內(nèi)存泄露的問(wèn)題,參考 JavaScript Closures 中的相關(guān)部分.

而在某些場(chǎng)合,你可能必須要使用closure, 如 循環(huán)問(wè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); } } //可惜的是,當(dāng)你點(diǎn)擊每個(gè)鏈接時(shí),輸出的都是 Link 4 // 使用closure 可以解決這個(gè)問(wèn)題 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); };//返回一個(gè)函數(shù) }(i);//調(diào)用這個(gè)函數(shù) document.body.appendChild(link); } } window.onload = addLink(4); window.onload = addLink2(4); 

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

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

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

具體的討論可見(jiàn): SO

這即是一個(gè)典型的closure應(yīng)用場(chǎng)景, 而如果不使用, 就無(wú)法解決這個(gè)問(wèn)題.

結(jié)論

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

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

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

總之, 關(guān)于closure,你必須記住以下幾點(diǎn):

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

上篇博文講的是 prototype, 下篇博文預(yù)計(jì)會(huì)講 this關(guān)鍵字, 歡迎大家討論和留言.

參考資料

  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 來(lái)閱讀.

JavaScript技術(shù)javascript 必知必會(huì)之closure,轉(zhuǎn)載需保留來(lái)源!

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

主站蜘蛛池模板: 人曽交Z00Z0OA片 | 99国产亚洲精品无码成人 | 久久久无码精品亚洲A片猫咪 | 97久久超碰中文字幕 | 国产人人为我我为人人澡 | 国产精品麻豆a啊在线观看 国产精品麻豆AV | 国产麻豆剧看黄在线观看 | 欧美在线亚洲综合国产人 | 黑人干日本美女 | 寂寞夜晚免费观看视频 | 精品久久久久久久高清 | 超级乱淫片午夜电影网99 | 国产欧美精品一区二区色综合 | 99午夜高清在线视频在观看 | 国产精华av午夜在线观看 | 少妇厨房愉情理9伦片视频 少妇被躁爽到高潮无码久久 | 公和我做好爽添厨房中文字幕 | 中文有码中文字幕免费视频 | 国产乱码精品一区二区三区四川 | 蜜桃视频无码区在线观看 | 欧美黄色精品 | 在线观看99| 成人免费一级毛片在线播放视频 | 亚洲国产精品一区二区动图 | 免费又黄又硬又爽大片 | 91精品婷婷国产综合久久8 | 一本久道视频无线视频 | 精品三级久久久久电影网1 精品日韩视频 | 亚洲国产欧美在线看片 | 日韩hd高清xxxⅹ | 亚洲中文字幕日产乱码2020 | 婚后被调教当众高潮H喷水 回复术士勇者免费观看全集 | 农民下乡在线观看3 | 男人J进女人P | younv 学生国产在线视频 | 毛片在线不卡 | 日韩精品无码免费专区 | 久久国产露脸老熟女熟69 | 蜜臀AV精品久久无码99 | 日韩亚洲国产中文字幕欧美 | 国内自拍 在线 亚洲 欧美 |