摘要:你可能經常看到這句話創建閉包的最常見的方式就是在一個函數內創建另一個函數,通過另一個函數訪問這個函數的局部變量。這種現象稱之為閉包。雖然中沒有類這樣的機制,但是通過使用閉包,我們可以模擬出這樣的機制。
JS 閉包
JS編程的時候你一定遇到過這個問題:局部變量實現累加,看下面例子:
function aotuadd(){ var a=1; a++; console.log(a); } aotuadd();//2 autuadd();//2
上面的代碼無法實現累加,這時可能有的人就會選擇把a放在全局作用域中,能實現累加功能,但是會使全局變量增多,這是我們不想看到的。
其實之所以把a放在全局作用域中,是因為autoadd函數的作用域被全局作用域包裹,所以我們可以在全局作用域中取值;
那么我們是不是可以給autoadd外層再包裹一個作用域(假設是wapper),然后將這個a放在wapper作用域中,問題不就解決了嘛。
我們既能訪問wrapper中的a,又不必增加全局變量。因為js中只有函數能夠產生作用域,所以其實就是再aotoadd外包裹一個wrapper函數,試著寫一下:
function wrapper(){ var a=1; function autoadd(){ a++; console.log(a); } } wrapper();
寫到這里發現,我們無法訪問autoadd,怎么解決:
function wrapper(){ var a=1; function autoadd(){ a++; console.log(a); } window.bar=autoadd; } wrapper() bar();//2 bar();//3
上面這種方法是能夠解決無法調用的問題的,但是這回到了我們最開始遇到的問題,增加了全局變量/函數,這是我們不想看到的;另一種解決方法:
function wrapper(){ var a=1; function autoadd(){//必要條件 a++;//必要條件 console.log(a); } return autoadd; } var x=wrapper()//返回一個函數,巧合的是,返回的這個函數體中,還有一個變量a要引用wrapper作用域下的a,所以這個a不能銷毀,wrapper()上下文環境不被銷毀,依然存在于執行上下文棧中; x(); x();
通過返回函數的方法進行調用,上面的這種寫法就是我們最常見的閉包的寫法,也就是說,閉包的產生,其實并不是一定依賴于“返回函數”這個條件,只不過不通過這種方法調用有違初衷;
看懂了上面這個例子,閉包的概念也呼之欲出:閉包是指有權訪問另一個函數作用域中的變量的函數,即當前作用域總能訪問外部作用域中的變量。
你可能經常看到這句話:“創建閉包的最常見的方式就是在一個函數內創建另一個函數,通過另一個函數訪問這個函數的局部變量”。其實我覺得恰恰就是這句話,導致很多人無法理解閉包,換成下面這種說法更好理解:創建閉包的最常見的方式是在一個函數外部包裹另一個函數,通過在另一個函數內部定義變量的方式,使我們想要的變量駐留在外層函數中,減少全局變量。
閉包的兩個必要條件:函數外層有函數/ 內層函數要使用外層函數中的變量
我們總結一下,上面var x=wrapper()和function warpper()可以利用立即執行函數合寫,進一步減少全局變量:
var x=(function(){ var a=1; return function(){ a++; console.log(a); } })(); x();//2 x();//3 x=null;//解除引用,等待垃圾回收
或者:
function Myobj(){ var age=1;; this.autoadd=function(){ age++; console.log(age); //return age; } } var obj=new Myobj(); obj.autoadd(); obj.autoadd();
另一個常見問題:
for循環給網頁中一連串元素綁定,例如onclick事件:
var fn = function() { var divs = document.querySelectorAll("div"); for (var i = 0; i < 3; i++) { divs[i].onclick = function() { alert(i); }; } }; fn();
點擊每個div都會彈出3。這是為什么呢?
我們先來分析一下原因:onclick事件是一個異步回調函數的指針,并不會立即執行,上面的函數表達式,并不會進行變量賦值。只有在調用一個函數時,一個新的執行上下文才會被創建出來。那么我們是不是可以通過調用函數的方法,來創建多個新的執行上下文環境,創建新的作用域,這樣不同的調用就可以有不同的參數。那么解決思路也是外層包裹function的方法,即利用閉包:
var fn = function() { var divs = document.querySelectorAll("div"); for (var i = 0; i < 3; i++) { divs[i].onclick = (function(a) {//立即執行函數,創建新的執行上下文 alert(a); })(i); } }; fn();
或者:
var fn = function() { var divs = document.querySelectorAll("div"); for (var i = 0; i < 3; i++) { (function(i){//立即執行函數,創建新的執行上下文,將i駐留在內存中 divs[i].onclick = function() { alert(i); })(i); } }; fn();
另一種解決方法:(利用事件代理)
var ul=document.querySelector("ul"); var lis=ul.querySelectorAll("ul li"); ul.addEventListener("click", function (e) { var target= e.target; if(target.nodeName.toUpperCase()==="LI"){ alert([].indexOf.call(lis,target)); } },false)
理解閉包的關鍵就是下面這句:
閉包:當一個函數在定義它的作用域以外的地方被調用時,它訪問的依然是定義它時的作用域。這種現象稱之為閉包。
JavaScript中的函數運行在它們被定義的作用域里,而不是它們被執行的作用域里。——《JavaScript語言精粹》
閉包的優點:
1)使變量駐留在內存中(多了變缺點);
2)避免全局變量污染;
3)私有化變量;
閉包的缺點:
1)因為閉包會攜帶包含它的函數的作用域,所以比其他函數占用更多內存;
2)使用不當會造成內存泄漏;
閉包應用場景(來自《javascript高級程序設計》)
1.使用閉包可以在JS中模擬塊級作用域(ECMAScript6標準之前的JavaScript本身沒有塊級作用域的概念);
function outputNumbers(count){ (function(){ for(var i = 0; i < count; i++){ alert(i); } })(); alert(i); //導致一個錯誤! }
2.閉包可以用于在對象中創建私有變量;
// 1.2.閉包可以用于在對象中創建私有變量
function MyObject(){ // 私有變量和私有函數 var privateVariable = 10; function privateFunction(){ return false; } // 特權方法,調用私有方法、函數 this.publicMethod = function(){ privateVariable++; return privateFunction(); } }
閉包的運用
1.匿名自執行函數
我們在實際情況下經常遇到這樣一種情況,即有的函數只需要執行一次,其內部變量無需維護,比如UI的初始化,那么我們可以使用閉包:
//將全部li字體變為紅色 (function(){ var els = document.getElementsByTagName("li"); for(var i = 0,lng = els.length;i < lng;i++){ els[i].style.color = "red"; } })();
我們創建了一個匿名的函數,并立即執行它,由于外部無法引用它內部的變量,
因此els,i,lng這些局部變量在執行完后很快就會被釋放,節省內存!
關鍵是這種機制不會污染全局對象。
2. 實現封裝/模塊化代碼
var person= function(){ //變量作用域為函數內部,外部無法訪問 var name = "default"; return { getName : function(){ return name; }, setName : function(newName){ name = newName; } } }(); console.log(person.name);//直接訪問,結果為undefined console.log(person.getName()); //default person.setName("jozo"); console.log(person.getName()); //jozo
3. 實現面向對象中的對象
這樣不同的對象(類的實例)擁有獨立的成員及狀態,互不干涉。雖然JavaScript中沒有類這樣的機制,但是通過使用閉包,
我們可以模擬出這樣的機制。還是以上邊的例子來講:
function Person(){ var name = "default"; return { getName : function(){ return name; }, setName : function(newName){ name = newName; } } }; var person1= Person(); print(person1.getName()); john.setName("person1"); print(person1.getName()); // person1 var person2= Person(); print(person2.getName()); jack.setName("erson2"); print(erson2.getName()); //person2
Person的兩個實例person1 和 person2 互不干擾!因為這兩個實例對name這個成員的訪問是獨立的 。
初學js很多理解不到位的地方,望批評指正!
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/92541.html
摘要:大名鼎鼎的閉包面試必問。閉包的作用是什么。看到閉包在哪了嗎閉包到底是什么五年前,我也被這個問題困擾,于是去搜了并總結下來。關于閉包的謠言閉包會造成內存泄露錯。閉包里面的變量明明就是我們需要的變量,憑什么說是內存泄露這個謠言是如何來的因為。 本文為饑人谷講師方方原創文章,首發于 前端學習指南。 大名鼎鼎的閉包!面試必問。請用自己的話簡述 什么是「閉包」。 「閉包」的作用是什么。 首先...
摘要:當初看這個解釋有點懵逼,理解成閉包就是函數中的函數了。里的閉包最近不滿足于只干前端的活,開始用起了。里的閉包最近在學習語言,讓我們來看一下語言里的閉包。在中,閉包特指將函數作為值返回的情況,被返回的函數引用了生成它的母函數中的變量。 本人開始接觸編程是從js開始的,當時網上很多人說閉包是難點,各種地方對閉包的解釋也是千奇百怪。如今開始接觸js以外的各種編程語言,發現不光是js,php、...
摘要:當初看這個解釋有點懵逼,理解成閉包就是函數中的函數了。里的閉包最近不滿足于只干前端的活,開始用起了。里的閉包最近在學習語言,讓我們來看一下語言里的閉包。在中,閉包特指將函數作為值返回的情況,被返回的函數引用了生成它的母函數中的變量。 本人開始接觸編程是從js開始的,當時網上很多人說閉包是難點,各種地方對閉包的解釋也是千奇百怪。如今開始接觸js以外的各種編程語言,發現不光是js,php、...
摘要:內部的稱為內部函數或閉包函數。過度使用閉包會導致性能下降。,閉包函數分為定義時,和運行時。循環會先運行完畢,此時,閉包函數并沒有運行。閉包只能取得外部函數中的最后一個值。事件綁定種的匿名函數也是閉包函數。而對象中的閉包函數,指向。 閉包概念解釋: 閉包(也叫詞法閉包或者函數閉包)。 在一個函數parent內聲明另一個函數child,形成了嵌套。函數child使用了函數parent的參數...
閱讀 3159·2021-10-14 09:42
閱讀 3564·2019-08-26 13:56
閱讀 3445·2019-08-26 11:59
閱讀 938·2019-08-23 18:00
閱讀 2197·2019-08-23 17:51
閱讀 3522·2019-08-23 17:17
閱讀 1479·2019-08-23 15:11
閱讀 5146·2019-08-23 15:05