詳解js閉包

2021-03-02 藍橋雲課精選

閉包(closure)是Javascript語言的一個難點,也是它的特色,很多高級應用都要依靠閉包實現。

閉包的特性

閉包有三個特性:

1.函數嵌套函數

2.函數內部可以引用外部的參數和變量

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


閉包的定義及其優缺點

閉包 是指有權訪問另一個函數作用域中的變量的函數,創建閉包的最常見的方式就是在一個函數內創建另一個函數,通過另一個函數訪問這個函數的局部變量

閉包的缺點就是常駐內存,會增大內存使用量,使用不當很容易造成內存洩露。

閉包是javascript語言的一大特點,主要應用閉包場合主要是為了:設計私有的方法和變量。

一般函數執行完畢後,局部活動對象就被銷毀,內存中僅僅保存全局作用域。但閉包的情況不同!

嵌套函數的閉包

function aaa() {      var a = 1;      return function(){       alert(a++)    };     }            var fun = aaa();      fun();// 1 執行後 a++,,然後a還在~      fun();// 2       fun = null;//a被回收!!

閉包會使變量始終保存在內存中,如果不當使用會增大內存消耗。


javascript的垃圾回收原理

(1)、在javascript中,如果一個對象不再被引用,那麼這個對象就會被GC回收;


(2)、如果兩個對象互相引用,而不再被第3者所引用,那麼這兩個互相引用的對象也會被回收。

使用閉包的好處

那麼使用閉包有什麼好處呢?使用閉包的好處是:

1.希望一個變量長期駐紮在內存中

2.避免全局變量的汙染

3.私有成員的存在


一、全局變量的累加

<script>
var a = 1;
function abc(){        a++;        alert(a);}abc();              //2
abc();            //3
</script>


二、局部變量

<script>
function abc(){        
           var a = 1;        a++;        alert(a);}abc();                       //2
abc();                    //2
</script>

那麼怎麼才能做到變量a既是局部變量又可以累加呢?


三、局部變量的累加

<script>
function outer(){        
          var x=10;        
          return function(){             //函數嵌套函數                x++;                alert(x);        }}
var y = outer();              //外部函數賦給變量y;
y();                 //y函數調用一次,結果為11,相當於outer()();
y();                //y函數調用第二次,結果為12,實現了累加
</script>


函數聲明與函數表達式

在js中我們可以通過關鍵字function來聲明一個函數:

<script>
function abc(){        alert(123);}abc();
</script>

我們也可以通過一個"()"來將這個聲明變成一個表達式:

<script>
(function (){        alert(123);})();                  
//然後通過()直接調用前面的表達式即可,因此函數可以不必寫名字;
</script>


四、模塊化代碼,減少全局變量的汙染

<script>
var abc = (function(){      //abc為外部匿名函數的返回值        var a = 1;        
       return function(){                a++;                alert(a);        }})();abc();    //2 ;調用一次abc函數,其實是調用裡面內部函數的返回值    
abc();    //3
</script>


五、私有成員的存在

<script>
var aaa = (function(){        
          var a = 1;        
          function bbb(){                a++;                alert(a);        }        function ccc(){                a++;                alert(a);        }        return {                b:bbb,             //json結構                c:ccc        }})();aaa.b();     //2
aaa.c()      //3
</script>


六.使用匿名函數實現累加

//使用匿名函數實現局部變量駐留內存中,從而實現累加

<script type="text/javascript"> function box(){    
      var age = 100;    
      return function(){          //匿名函數          age++;          
              return age;     };     } var b = box();alert(b());alert(b());    //即alert(box()());
alert(b());
alert(b);            //     function () {                        //   age++;                       // return age;                      //       }
b = null;  //解除引用,等待垃圾回收
</script>

過度使用閉包會導致性能的下降。函數裡放匿名函數,則產生了閉包


七、在循環中直接找到對應元素的索引

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">    <head>            <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />            <title></title>    <script>    window.onload = function(){            
                var aLi = document.getElementsByTagName('li');                          for (var i=0;i<aLi.length;i++){                    aLi[i].onclick = function(){  //當點擊時for循環已經結束                    alert(i);                    };            }    }    
   </script>                </head>    <body>            <ul>                    <li>123</li>                    <li>456</li>                    <li>789</li>                    <li>010</li>            </ul>    </body>    </html>


八、使用閉包改寫上面代碼

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">    <head>            <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />            <title></title>    <script>    window.onload = function(){            
              var aLi = document.getElementsByTagName('li');                          for (var i=0;i<aLi.length;i++){                    (function(i){                            aLi[i].onclick = function(){                                    alert(i);                            };                    })(i);            }            };    
       </script>                </head>    <body>            <ul>                    <li>123</li>                    <li>456</li>                    <li>789</li>            </ul>    </body>    </html>


九.內存洩露問題

由於IE的js對象和DOM對象使用不同的垃圾收集方法,因此閉包在IE中會導致內存洩露問題,也就是無法銷毀駐留在內存中的元素

function closure(){    
   var oDiv = document.getElementById('oDiv');//oDiv用完之後一直駐留在內存中    oDiv.onclick = function () {        alert('oDiv.innerHTML');//這裡用oDiv導致內存洩露    };}closure();//最後應將oDiv解除引用來避免內存洩露
function closure(){    
     var oDiv = document.getElementById('oDiv');    
     var test = oDiv.innerHTML;    oDiv.onclick = function () {        alert(test);    };    oDiv = null;}

轉載自:segmentfault

文章地址:https://segmentfault.com/a/1190000000652891

作者:trigkit4 

找不到女朋友?來學習啊,點擊菜單【技術教程】,成為技術大牛,出任CEO,贏取白富美~


相關焦點

  • 詳解 js 閉包
    閉包的特性閉包有三個特性:1.函數嵌套函數2.函數內部可以引用外部的參數和變量3.參數和變量不會被垃圾回收機制回收閉包的定義及其優缺點閉包 是指有權訪問另一個函數作用域中的變量的函數,創建閉包的最常見的方式就是在一個函數內創建另一個函數,通過另一個函數訪問這個函數的局部變量
  • 詳解 JavaScript 閉包
    閉包的特性閉包有三個特性:1.函數嵌套函數2.函數內部可以引用外部的參數和變量3.參數和變量不會被垃圾回收機制回收閉包的定義及其優缺點閉包 是指有權訪問另一個函數作用域中的變量的函數,創建閉包的最常見的方式就是在一個函數內創建另一個函數
  • 大部分人都會做錯的經典 JS 閉包面試題
    如果都答對了恭喜你在js閉包問題當中幾乎沒什麼可以難住你了;如果沒有答案,繼續往下分析。JS中有幾種函數首先,在此之前需要了解的是,在JS中函數可以分為兩種,具名函數(命名函數)和匿名函數。遂:在第一次調用fun(0)時,o為undefined;第二次調用fun(1)時m為1,此時fun閉包了外層函數的n,也就是第一次調用的n=0,即m=1,n=0,並在內部調用第一層fun函數fun(1,0);所以o為0;第三次調用fun(2)時m為2,但依然是調用a.fun,所以還是閉包了第一次調用時的n,所以內部調用第一層的fun(2,0);
  • 史上最強-JavaScript閉包原理詳解-從入門到放棄
    閉包這個東西,很有意思,不管在什麼程式語言中,我總能遇到閉包這個玩意(實際是 接觸的語言少,運氣不好),所以啊,閉包這個小可愛,並不是某一種程式語言特有的,就像變量、函數、作用域這些,在幾乎所有程式語言中都有存在;但,在不同的程式語言中,這個小可愛的設計理念和實現邏輯又有所不同,所以想要更好的理解閉包
  • 為什麼向後端工程師推薦Node.js
    js的一些認識,以及我作為前端工程師為什麼會向後端工程師推薦Node.js。「Node.js 是伺服器端的 JavaScript 運行環境,它具有無阻塞(non-blocking)和事件驅動(event-driven)等的特色,Node.js 採用V8引擎,同樣,Node.js實現了類似 Apache 和 nginx 的web服務,讓你可以通過它來搭建基於 JavaScript的Web App。」
  • 什麼是閉包?一分鐘帶你了解!
    作者:茄果原文:www.cnblogs.com/qieguo/p/5457040.html什麼是閉包有權訪問另一個函數作用域內變量的函數都是閉包。這裡 inc 函數訪問了構造函數 a 裡面的變量 n,所以形成了一個閉包。
  • JavaScript同步、異步、回調執行順序之經典閉包setTimeout面試題分析
    這裡涉及到JavaScript執行棧和消息隊列的概念,概念的詳細解釋可以看阮老師的 JavaScript 運行機制詳解:再談Event Loop – 阮一峰的網絡日誌,或者看 並發模型與Event Loop《圖片來自於MDN官方》我拿這個例子做一下講解,JavaScript單線程如何處理回調呢?
  • 三年經驗的前端,如何理解JS三座大山
    本文轉載自【微信公眾號:前端人,ID:FrontendPeople】經微信公眾號授權轉載,如需轉載與原文作者聯繫前言本篇文章比較適合 3 年以上的前端工作者,JS 三座大山分別指:原型與原型鏈,作用域及閉包,異步和單線程。
  • 用9種辦法解決 JS 閉包經典面試題之 for 循環取 i
    轉自 https://segmentfault.com/a/1190000003818163閉包1.正確的說,應該是指一個閉包域
  • 五、閉包
    初學JavaScript時,我在閉包上,走了很多彎路。而這次重新回過頭來對基礎知識進行梳理,要講清楚閉包,也是一個非常大的挑戰。閉包有多重要?如果你是初入前端的朋友,我沒有辦法直觀的告訴你閉包在實際開發中的無處不在,但是我可以告訴你,前端面試,必問閉包。
  • 原來JavaScript的閉包是這麼回事!
    所以,肯定存在另一種被我們忽略的機制——也就是閉包。下面是它的工作原理。每當聲明一個新函數並將其賦值給變量時,實際上是保存了函數定義和閉包。閉包包含了創建函數時聲明的所有變量,就像一個背包一樣——函數定義附帶一個小背包。這個背包保存了創建函數時聲明的所有變量。
  • python中的閉包
    首先來看閉包的概念:簡單來說,閉包的概念就是當我們在函數內定義一個函數時,這個內部函數使用了外部函數的臨時變量,且外部函數的返回值是內部函數的引用時,我們稱之為閉包。def outer(a, b): def inner(x): return a + x + b return innerm1 = outer(1, 4)m2 = outer(4, 9)print m1(10), m2(5)輸出:注意:python當中函數是一個對象,所以可以作為某個函數的返回結果使用閉包時
  • 理解閉包與內存洩漏
    被包裹的函數則稱為閉包函數,包裹的函數(外部的函數)則為閉包函數提供了一個閉包作用域,所以形成的閉包作用域的名稱為外部函數的名稱。函數提供的作用域,閉包作用域內有一個變量bar可以被閉包函數訪問到。二、形成閉包的條件從上面的閉包例子在,看起來形成的閉包的條件就是,一個函數被另一個函數包裹,並且返回這個被包裹的函數供外部持有。
  • PHP與Node.js:一個史詩般開發者的分享
    一種語言既可以構建Node.js,也是客戶端運行框架所需要的。「JavaScript無處不在」成為了一些人的口頭禪。  從那時起,兩個平臺都不斷地擴大和改進。Node.js的開發人員現在可以選擇一個不斷擴充的優秀框架集合:Express,Angular,Meteor等。列表很長,最大的問題是選擇最優秀最合適的。
  • 聊聊Python中的閉包
    來源: rainyear 連結:https://github.com/rainyear/pytips/blob/master/Markdowns/2016-03-10-Scope-and-Closure.md(點擊尾部閱讀原文前往)閉包
  • python進階教程之閉包
    閉包1.什麼是閉包#定義一個函數def test(number):#在函數內部再定義一個函數,並且這個函數用到了外邊函數的變量,那麼將這個函數以及用到的一些變量稱之為閉包def test_in(number_in):print("in test_in 函數, number_in is %d"%number_in)return
  • 說說Python中閉包是什麼?
    廢話不多說,開始今天的題目:問:說說Python中閉包是什麼?答:可以將閉包理解為一種特殊的函數,這種函數由兩個函數的嵌套組成,外函數和內函數。在一個外函數中定義了一個內函數,內函數裡運用了外函數的臨時變量,並且外函數的返回值是內函數的引用。這樣就構成了一個閉包。
  • OC與Swift閉包對比總結
    Swift的閉包OC中的__block是一個很討厭的修飾符。它不僅不容易理解,而且在ARC和非ARC的表現截然不同。__block修飾符本質上是通過截獲變量的指針來達到在閉包內修改被截獲的變量的目的。在Swift中,這叫做截獲變量的引用。閉包默認會截取變量的引用,也就是說所有變量默認情況下都是加了__block修飾符的。
  • JS基礎知識總結
    實例對象的原型指向也會改變原型的指向是可以改變的原型指向改變後添加原型方法才可以用實例對象中的屬性或方法,先在實例中查找,再到原型對象中查找要改變原型中的屬性直接通過原型.屬性=值對象.屬性,只要點了 ,對象就有了這個屬性,只是沒有賦值,所以是undefineddiv 的原型鏈繼承:繼承是一種類與類之間的關係,js
  • 前端基礎進階(五):JavaScript 閉包詳細圖解
    而這次重新回過頭來對基礎知識進行梳理,要講清楚閉包,也是一個非常大的挑戰。閉包有多重要?如果你是初入前端的朋友,我沒有辦法直觀的告訴你閉包在實際開發中的無處不在,但是我可以告訴你,前端面試,必問閉包。面試官們常常用對閉包的了解程度來判定面試者的基礎水平,保守估計,10個前端面試者,至少5個都死在閉包上。