如何理解javascript closure ?


  接觸過javascript的人應該聽過閉包(closure),有一種觀點認為是閉包賦予了javascript的強大能力,也賦予了它具備OOP的特征。既然javascript closure如此重要,那么問題來了,什么是closure呢?closure有什么作用?本文將結合我自己對closure的理解,用盡量通俗易懂的方式來進行闡述。

  先看看老外對closure怎么定義的?A closure is an inner function that has access to the outer (enclosing) function's variables—scope chain.從字面上來看,可以翻譯為:閉包就是一個內部函數,它具備訪問外部函數變量(這些變量位於作用域鏈中)的能力[注意變量不包含this和arguments]。但這個概念還是過於“專業化”, 不接地氣,只從概念上來看,非常抽象。下面用一段JS來對應解釋閉包的概念:


個人理解為:fInner函數定義在fOuter的內部,可以訪問fOuter定義的變量,這個fInner函數就是closureclosure的 scope chain有3個一個是fInner函數定義的變量;一個是外部fOuter的變量;還有一個是全局變量(global variables)。closure的作用可以保留變量的值換句話說,函數fInner就是一個closure

  closure可以干2件事情:

  1)closure可以調用(閉包存儲的外部變量是引用而不是值,這點非常重要)在當前函數以外的定義的變量(即使外部函數已經返回);

  2)closure可以修改外部定義的變量值

下面通過一段JS代碼來闡述為什么閉包存儲的外部變量是引用而不是值,這點非常重要

 

 1     function mixFunction(a)  2  {  3         var result=[],i,n;  4         n=a.length;  5         for(i=0;i<n;i++){  6             result[i]=function(){  7                 //Closure對外部變量是引用
 8                 console.log("for i="+i);  9                 return a[i];//a[i-1]
10  } 11  } 12         return result; 13  } 14     var mixcall=mixFunction([10,20,30]); 15     var f=mixcall[0]; 16     console.log(f());//?應該輸出什么值

 

f()會輸出 10 么?答案是undefined!!!

為什么是這樣的呢? 因為閉包對i是引用,在for循環時,會不斷更新i的值。即在調用f()后,i已經被修改為3 (i===3),而a[3]為undefined!!

上述的代碼始終會輸出a[2]的值,也就是30。

 那么問題來了?怎么解決該問題呢?這就要用到JS中的IIFE技術啦!IIFE技術來解決JS缺少塊級作用域的解決方案如下代碼所示:

 1     function mixFunctionFix(a)  2  {  3         var result=[],i,n;  4         n=a.length;  5         for(i=0;i<n;i++){  6             //IIFE技術來解決JS缺少塊級作用域的解決方案
 7             (function(j){  8                 result[i]=function(){  9                     //Closure對外部變量是引用
10                     console.log("for j="+j); 11                     return a[j]; 12  } 13  })(i) 14  } 15         return result; 16  } 17     var mixcall=mixFunctionFix([10,20,30]); 18     var f=mixcall[0]; 19     console.log(f());//10

 前面提到JS閉包可以實現OOP的特征,下面給出JS如何定義一個“User類”(實際是一個JS函數)。此函數有私用變量和方法,同時也具有公開的方法。私有變量和方法不能函數外部調用:

 1     function User(){  2         //private properties
 3         var _name = '';  4         var _age  = 0;  5      
 6         //private method
 7         function privateMethod(){  8             return "private  Method"
 9  }; 10         //public method
11         this.setName = function(name){ 12             //can call
13  privateMethod(); 14             _name = name; 15             return this; 16  }; 17      
18         this.setAge = function(age){ 19             _age = age; 20             return this; 21  }; 22         this.getAge = function(){ 23             return _age; 24  }; 25         this.getName = function(){ 26             return _name; 27  }; 28  } 29     var p=new User(); 30     console.log(p.setName('jackwang').setAge(28)); 31     console.log(p.getName());//jackwang
32     console.log(p.getAge());//28
33     console.log(p._name);//undefined 
34     console.log(p.privateMethod());//error

私有變量_name和_age以及私有方法privateMethod()都不能在函數外部調用。User設置name和age只能通過公開方法setName和setAge進行,獲取只能通過getName和getAge進行訪問。

有人會問,這個是閉包么?看下圖:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM