你的位置:首页 > Java教程

[Java教程]如何理解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.从字面上来看,可以翻译为:闭包就是一个内部函数,它具备访问外部函数变量(这些变量位于作用域链中)的能力。但这个概念还是过于“专业化”, 不接地气,只从概念上来看,非常抽象。下面用一段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 method11     this.setName = function(name){12       //can call13       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());//jackwang32   console.log(p.getAge());//2833   console.log(p._name);//undefined 34   console.log(p.privateMethod());//error

私有变量_name和_age以及私有方法privateMethod()都不能在函数外部调用。User设置name和age只能通过公开方法setName和setAge进行,获取只能通过getName和getAge进行访问。

有人会问,这个是闭包么?看下图: