亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > JS > 正文

JavaScript閉包原理與用法實例分析

2024-05-06 16:45:37
字體:
來源:轉載
供稿:網友

本文實例講述了JavaScript閉包原理與用法。分享給大家供大家參考,具體如下:

1、與閉包有關的兩個概念:

1) 變量的作用域

不帶有關鍵字var的變量會成為全局變量;

在函數中使用關鍵字var聲明的變量是局部變量。

局部變量只有在函數內部才能訪問到,在函數外面是訪問不到的。但在函數內部可以通過作用域鏈一直向上搜索直到全局對象,也就是說,函數內部可以訪問函數外部的變量。

2) 變量的生存周期

對于全局變量,其生存周期是永久的,除非主動銷毀這個全局變量;

而對于在函數內用關鍵字var聲明的局部變量,當退出函數時,這些局部變量會隨著函數調用結束而被銷毀。

var func = function() {  var i = 1;  alert(i); // 輸出:1};alert(i); // 報錯:i is not defind.

例外情況:閉包

var func = function() {  var i = 1;  return function() {    alert(i);    i++;  }};var f1 = func();f1(); // 輸出:1f1(); // 輸出:2var f2 = func();f2(); // 輸出:1f2(); // 輸出:2

2、從閉包的一個經典應用談起

<div>0</div><div>1</div><div>2</div><div>3</div><div>4</div><script>  var divs = document.getElementsByTagName("div");  for (var i = 0; i < divs.length; i++) {    divs[i].onclick = function() {      alert(i);    };  }</script>

問題:無論單擊哪個div,都會彈出5。

原因:onclick事件是異步觸發的,當事件被觸發時,for循環早已結束,此時變量i的值早已經是5。

解決:在閉包的幫助下,把每次循環的i值都封閉起來。當事件函數順著作用域鏈從內到外查找變量i時,會先找到被封閉在閉包環境的i,單擊div時,會分別輸出0,1,2,3,4。

<div>0</div><div>1</div><div>2</div><div>3</div><div>4</div><script>var divs = document.getElementsByTagName("div");for (var i = 0; i < divs.length; i++) {  divs[i].onclick = (function(num) {    return function() {      alert(num);    };  })(i);}</script>

類似實例:閉包直接賦給數組

function createFunctions() {  var result = new Array();  for (var i = 0; i < 10; i++){    result[i] = function(){      return i;    };  }  return result;}for (var i = 0; i < 10; i++)  alert(createFunctions()[i]());

結果:result的每個元素都返回10。

說明:閉包的作用域鏈有明顯的副作用——閉包總是獲得外部函數變量的最終值。上面代碼中,外部函數產生一個函數數組result并返回。函數數組中的每個元素都是一個函數,每個函數都返回 i變量。似乎每個函數應該返回每次循環的i值,即依次返回0到9,但事實是,每個函數的返回結果都是10。這是因為每個內部函數返回的是變量i,而不是i在某個時刻的特定值,而i的作用域是整個外部函數,當外部函數執行完成后,i的值是10。

解決:在每個內部函數的內部,再產生一個匿名函數并返回。

function createFunctions() {  var result = new Array();  for (var i = 0; i < 10; i++) {    result[i] = (function(num) {      return function() {        return num;      };    })(i);  }  return result;}for (var i = 0; i < 10; i++)  alert(createFunctions()[i]());

結果:result依次返回0到9。

說明:(i)使得該層匿名函數立即執行。

3、閉包

有時候需要得到函數內的局部變量。如何從外部讀取局部變量?那就是在函數的內部,再定義一個函數。

閉包是指有權訪問另一個函數作用域中變量的函數,創建閉包的最常見的方式就是在一個函數內創建另一個函數,通過另一個函數訪問這個函數的局部變量,利用閉包可以突破作用鏈域,將函數內部的變量和方法傳遞到外部。

① 閉包的原理

1) 后臺執行環境中,閉包的作用域鏈包含著自己的作用域、函數的作用域和全局作用域。

2) 通常,函數的作用域和變量會在函數執行結束后銷毀。

3) 但是,當函數返回一個閉包時,這個函數的作用域將會一直在內存中保存到閉包不存在為止。

② 閉包的特性

1) 函數內再嵌套函數。

2) 內部函數可以引用外層的參數和變量。

3) 參數和變量不會被垃圾回收機制回收。

③ 閉包的用途

1) 讀取函數內部的變量。

function f1(){  var n = 999;  function f2(){    alert(n);//999  }}

在上面的代碼中,函數f2就被包括在函數f1內部,這時f1內部的所有局部變量,對f2都是可見的。但是反過來就不行,f2內部的局部變量,對f1就是不可見的。既然f2可以讀取f1中的局部變量,那么只要把f2作為返回值,就可以在f1外部讀取它的內部變量了。

function f1(){  var n = 999;  function f2(){    alert(n);  }  return f2;}var result=f1();result();//999

閉包就是能夠讀取其他函數內部變量的函數。由于在Javascript語言中,只有函數內部的子函數才能讀取局部變量,因此可以把閉包簡單理解成"定義在一個函數內部的函數"。所以,在本質上,閉包就是將函數內部和函數外部連接起來的一座橋梁。

function f1(){  var n = 999;  nAdd = function(){n += 1}  function f2(){    alert(n);  }  return f2;}var result=f1();result();//999nAdd();result();//1000

result實際上就是閉包f2函數。它一共運行了兩次,第一次的值是999,第二次的值是1000。這證明了,函數f1中的局部變量n一直保存在內存中,并沒有在f1調用后被自動清除。原因就在于f1是f2的父函數,而f2被賦給了一個全局變量,這導致f2始終在內存中,而f2的存在依賴于f1,因此f1也始終在內存中,不會在調用結束后,被垃圾回收機制回收。

2) 讓函數內部的變量的值始終保持在內存中(延長局部變量的壽命)。

var print, add, set;function closure() {  var number = 8;  print = function() {    alert(number);  }  add = function() {    number++;  }  set = function(x) {    number = x;  }}closure();//創建一個閉包add();print();//9set(0);print();//0var oldClosure = print;closure();//創建一個新的閉包print();//8oldClosure();//0

使用閉包的注意點:由于閉包會使得函數中的變量都被保存在內存中,內存消耗很大,所以不能濫用閉包,否則會造成網頁的性能問題,在IE中可能導致內存泄露。解決方法是,在退出函數之前,將不使用的局部變量全部刪除。也就是說,閉包會引用外部函數作用域,會占用更多的內存,過度使用閉包,會導致性能問題。所以,僅當必要時才使用閉包。對產生閉包的函數,使用后應該解除引用。

3)自執行函數+閉包減少全局變量污染(封裝私有變量)

var person = (function() {  var_name = "Alice";  var _id = 16;  return {    getUserInfo: function() {      return _name + ": " + _id;    }  }})();

使用下劃線來約定私有變量_name和_age,它們被封裝在閉包產生的作用域中,外部是訪問不到這兩個變量的,這就避免了對全局的命令污染。

④ 閉包的缺點:

1) 需要維護額外的作用域。

2) 過渡使用閉包會占用大量內存。

4、this對象

在閉包內使用this對象將產生一些復雜的行為。this對象的值基于函數所在的執行環境在運行時決定:在全局函數中使用時,this等于window(非嚴格模式)或undefined(嚴格模式);而當作為對象的方法調用時,this等于這個對象。

var name = "The window";var object = {  name: "My object",  getName: function() {    return function() {      return this.name;    };  }};alert(object.getName()());//輸出:"The window"

每個函數一旦被調用,它將自動獲得this和arguments兩個變量。一個內部函數是不能直接從外部函數訪問到這兩個變量的。可以通過將this對象存儲在另一個變量中來解決這個問題。把外部作用域中的this對象保存在一個閉包能夠訪問到的變量里,就可以讓閉包訪問該對象了。

var name ="The window";var object = {  name: "My object",  getName: function() {    var that = this;    return function() {      return that.name;    };  }};alert(object.getName()());//輸出:"My object"

要讓閉包訪問外部函數的this和arguments對象,可以通過將它們的引用存儲在另一個變量中來完成。

5、內存泄漏

使用閉包的時候很容易造成循環引用,若閉包的作用域包含著一些DOM節點,這時候就有可能造成內存泄漏,但其實,這本身不是閉包的問題,而是由于:BOM和DOM中的對象是使用C++以COM對象的方式實現的,而COM對象的垃圾收集機制采用的是引用計數策略,在基于引用計數策略的垃圾回收機制中,若兩個對象之間形成了循環引用,則這兩個對象都無法被回收。

function assignHandler() {  var element = document.getElementById("id");  element.onclick = function() {    alert(element.id);  }}

匿名函數保存了一個對element的引用,只要匿名函數存在,element的引用數至少為1,它所占用的內存就永遠不會被回收。

function assignHandler() {  var element = document.getElementById("id");  var id = element.id;  element.onclick = function() {    alert(id);  }  element = null;}

通過把element.id的一個副本保存在變量中,并且在閉包中引用該變量消除了循環引用,但是僅僅做到這一步還不能解決內存泄漏的問題,閉包會引用包含函數的所有活動對象,包含element,即使閉包不直接引用element,包含函數的活動對象中也仍然會保存一個引用,因此有必要把element元素設置為null,這樣就能解除對DOM對象的引用,確保正?;厥掌湔加玫膬却?。

6、模仿塊級作用域

JavaScript中沒有直接的塊級作用域。

function output(count) {  for (var i = 0; i < count; i++) {    alert(i);  }  alert(i);//輸出:10}

使用閉包可以模仿塊級作用域——創建并立即調用一個函數,這樣既可以執行其中的代碼,又不會在內存中留下對該函數的引用。結果是內部函數的所有變量都會立即被銷毀,除非將某些變量賦值給了包含作用域(即外部作用域)中的變量。

用作塊級作用域的匿名函數:將函數聲明包含在一對圓括號中,表示它實際上是一個函數表達式,緊隨其后的另一對圓括號會立即調用這個函數。

(function() {  //這里是塊級作用域;}) ();

可以使用匿名函數表達式來模擬塊級作用域,任何定義在匿名函數中的變量在匿名函數執行完之后都將被銷毀,在匿名函數外訪問這些變量將會產生錯誤。

function output(count) {  (function() {    for (var i = 0; i < count; i++) {      alert(i);    }  }) ();  alert(i);//出錯}

希望本文所述對大家JavaScript程序設計有所幫助。


注:相關教程知識閱讀請移步到JavaScript/Ajax教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲黄一区二区| 亚洲激情免费观看| 综合久久五月天| 欧美日韩在线视频一区二区| 91精品啪在线观看麻豆免费| 精品久久久久久中文字幕大豆网| 亚洲自拍在线观看| 91在线观看免费高清完整版在线观看| 中文字幕一区二区三区电影| 国产欧美在线观看| 91sao在线观看国产| 1769国内精品视频在线播放| 中文字幕亚洲无线码在线一区| 在线日韩精品视频| 亚洲精品福利在线观看| 视频在线一区二区| 日韩美女在线播放| 久久久999国产精品| 欧美亚洲日本黄色| 亚洲色图美腿丝袜| 中文字幕亚洲天堂| 爽爽爽爽爽爽爽成人免费观看| 国产日韩在线精品av| 国产一区二区三区毛片| 国内免费久久久久久久久久久| 国产精品久久在线观看| 欧美伊久线香蕉线新在线| 不用播放器成人网| 色狠狠久久aa北条麻妃| 中文字幕国内精品| 77777少妇光屁股久久一区| 国产精品美女在线| 成人网页在线免费观看| 国产欧美一区二区三区在线| 欧美日韩在线第一页| 中文字幕久热精品视频在线| 亚洲毛片在线观看| 伊人一区二区三区久久精品| 久久精品亚洲精品| 国产精品成人免费视频| 日韩av中文字幕在线| 2018中文字幕一区二区三区| 久久亚洲综合国产精品99麻豆精品福利| 欧美一级bbbbb性bbbb喷潮片| 欧美日韩国产成人| 欧美亚洲国产视频小说| 国产日韩在线视频| 超碰精品一区二区三区乱码| 色狠狠久久aa北条麻妃| 亚洲人在线视频| 中国china体内裑精亚洲片| 国产精自产拍久久久久久| 亚洲国产精品999| 日韩精品在线免费观看| 亚洲人成在线观| 亚洲白虎美女被爆操| 午夜欧美大片免费观看| 欧美日韩在线视频一区| 精品亚洲一区二区三区在线观看| 精品久久久久久中文字幕| 欧美孕妇毛茸茸xxxx| 国产99视频在线观看| 欧美激情xxxx性bbbb| 一区二区三区日韩在线| 国产欧美 在线欧美| 精品人伦一区二区三区蜜桃网站| 欧美激情乱人伦一区| 91精品国产九九九久久久亚洲| 97成人精品视频在线观看| 91精品在线播放| 日韩在线观看精品| 狠狠躁天天躁日日躁欧美| 91亚洲精品在线| 国产69精品久久久久99| xxxx欧美18另类的高清| 久青草国产97香蕉在线视频| 欧美插天视频在线播放| 亚洲人成毛片在线播放| 欧美孕妇与黑人孕交| 欧美黑人性生活视频| 一区二区国产精品视频| 欧美激情精品久久久久久蜜臀| 色婷婷av一区二区三区在线观看| 欧美大胆在线视频| 国产精品高潮呻吟视频| 日av在线播放中文不卡| 国产一区二区三区网站| 欧美xxxx14xxxxx性爽| 国产成人精彩在线视频九色| 日韩在线视频导航| 国产精品一区二区久久久久| 欧美专区福利在线| 欧美黑人xxxx| 一区二区三区视频免费在线观看| 久久精品免费电影| 黑人巨大精品欧美一区二区免费| 日韩欧美a级成人黄色| 国产精品情侣自拍| 久久久精品欧美| 精品久久久久久亚洲国产300| 成人性教育视频在线观看| 成人在线观看视频网站| 国产精品福利在线| 国产精品久久在线观看| 91免费在线视频网站| 亚洲欧美日韩精品久久| 欧美激情一级二级| 一区二区三区视频在线| 日韩免费av片在线观看| 久久天天躁夜夜躁狠狠躁2022| 国产精品高潮呻吟久久av野狼| 日韩少妇与小伙激情| 欧洲中文字幕国产精品| 国产精品com| 日本久久久久久久久| 精品亚洲va在线va天堂资源站| 久久久之久亚州精品露出| 一本大道亚洲视频| 欧美不卡视频一区发布| 最近2019中文免费高清视频观看www99| 成人欧美在线观看| 成人免费视频网| 777精品视频| 日韩色av导航| 粗暴蹂躏中文一区二区三区| 欧美精品电影在线| 欧美激情免费视频| 69视频在线播放| 国产精品日韩欧美大师| 97在线视频免费播放| 亚洲伊人成综合成人网| 欧美激情在线有限公司| 日韩精品免费电影| 青青久久av北条麻妃黑人| 日韩精品有码在线观看| 欧美成年人视频网站欧美| 成人97在线观看视频| 国产国语videosex另类| 欧美国产亚洲精品久久久8v| 亚洲人成免费电影| 亚洲欧洲成视频免费观看| 国内外成人免费激情在线视频网站| 欧美激情综合色综合啪啪五月| 久久久久久av| 国产精品扒开腿做爽爽爽男男| 欧美日韩午夜激情| 亚洲韩国日本中文字幕| 欧美中文字幕在线播放| 久久天天躁狠狠躁夜夜躁| 久久精品国产91精品亚洲| 久久五月情影视| 日韩欧美在线国产| 国产一区二区三区在线观看网站| 国产亚洲精品久久| 久久69精品久久久久久久电影好| 亚洲三级黄色在线观看| 日本久久久久亚洲中字幕| 国产一区二区三区精品久久久| 亚洲人高潮女人毛茸茸| 久久久噜噜噜久久久| 久久综合伊人77777| 亚洲成人久久网| 日韩hd视频在线观看| 欧美在线播放视频|