首页 > 代码库 > js 闭包

js 闭包

初学者,本博文只是为自己学习而总结,参考者注意其中可能会有错误哦。

一 为什么用闭包

 

(一)访问局部变量

     参考 http://www.jb51.net/article/24101.htm

  一个内部函数除了可以访问自己的参数变量,还可以访问把它嵌套其中的父函数的参数和变量。

  但是,一个外部函数却无法访问内部函数的变量。

  function f1(){
    var n=999;
  }

  alert(n); // error

 

  我们有时候需要得到函数内的局部变量。因此,在函数的内部,再定义一个函数。

  看下面这个例子

  function f1(){

    n=999;  //f1不能访问f2的局部变量

    function f2(){
      alert(n); //f2也可以访问f1的变量 999
    }

  }

   

  既然f2可以读取f1中的局部变量,那么只要把f2作为返回值,我们就可以在f1外部读取它的内部变量。

  function f1(){

    n=999;

    return function (){
      alert(n);
    }

    

  }

  var result=f1();

  result(); // 999

 

 (二)安全性

   假设我们有一个对象为student,其中有一个属性为name,要访问stu1,最简单的做法可能是

    var Student = function (name){
       this.name = name;
    }  //构造一个Student的构造函数
      var stu1 = new Student("xiaoming");
       //调用构造函数 创建一个实例stu1;
    alert(stu1.name);    //访问stu1的属性
   }

  但是这样,外部就可以任意访问stu1的内部属性,谈不上安全性。

   我们可能会想到java中的做法,就是增加一个get set 方法

      var Student = function (name){
       this.name = name;
       this.getName = function(){
           return this.name;
           }
    }  //构造一个Student的构造函数
      var stu1 = new Student("xiaoming");
       //调用构造函数 创建一个实例stu1;
    alert(stu1.name);    //访问stu1的属性  弹出 xiaoming
    alert(stu1.getName());   //访问stu1的方法  弹出 xiaoming

    非常遗憾,还是不ok,因为js无法像java一样设置 private name;public getName

      (当然 去掉this.name = name,只保留getName方法好像在这个例子中可以达到目的,但毕竟不是长久之策)

   但这个思路是可以的,下面就是如何模拟私有变量和公有方法。

    我们可能会想到像上文一样,return一个函数(方法),这样,函数外部的可以访问这个return的内部函数(即方法),而不能访问这个函数本身的属性。

      var Student = function (name){
       var name = name;
       //返回一个方法
       return{          
           getName:function(){
               return name;
               }
           }; } ;
    
     //构造一个Student的构造函数
      var stu1 = new Student("xiaoming");
       //调用构造函数 创建一个实例stu1;
    alert(stu1.name);    //访问stu1的属性 错误
    alert(stu1.getName());   //弹出小明

         var stu2 = Student("xiaohong");
       //调用构造函数 创建一个实例stu1;
    alert(stu2.name);                 //错误
    alert(stu2.getName());    //弹出小红
   }

 

 

在这个例子中,我们调用Student ,返回一个包含getName方法的一个新对象,该对象的引用保存在stu1 stu2中,即使student已经返回来,但getName仍然可以访问student对象name属性

内部函数的生命周期要比外部函数的长

 此外 还有来自知乎的另一个例子,不再赘述。

 http://www.zhihu.com/question/19554716

 

另外还要说一点:上文返回的方法(getName)访问的并不是属性(name)的副本 而是其(name)本身。

举另外一个例子。

有一列表

<ul>
<li>0</li>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>

要实现 点击0 弹出0 点击1 弹出1.。。。。

很容易写成这样

 var ali = document.getElementsByTagName("li");
 var i;
 for(i=0;i<5;i++){
     ali[i].onclick = function(){
         alert(i);
         }
       }
 但事实上无论点击哪一个,都会弹出4

 这是因为,onclick函数访问的是i本身,而不是函数在构造是i的拷贝,所以每次都会弹出i;

可以这样修改

 var ali = document.getElementsByTagName("li");
 var i;
 var help = function(i){
     return function(){
         alert(i);
         }
     }
 for(i=0;i<5;i++){
     ali[i].onclick = help(i);
 }
   }
   循环之外创造一个辅助函数,让辅助函数返回一个绑定了i的值。

上面是大牛们给出的方法,但个人比较偏爱下面一种

 var ali = document.getElementsByTagName("li");
 var i;
 for(i=0;i<5;i++){
     ali[i].index = i;
     ali[i].onclick = function(){
         alert(this.index);
         }
       }
}

 

为列表添加一个index属性,同样可以达到目的。

 

 

最后 我们可以用闭包模拟模块。

“模块,一个定义了私有变量和函数的函数,利用闭包创建可以访问私有变量和函数的特权函数,最后返回这个特权函数”

这样可以摒弃全局变量

 


  

 

 

 

 

 

--------------------------------------------------------------------------------------------------------

 

 

 

 

 

 

 

 

 其他

 

一  变量作用域()

 (一) “一个内部函数除了可以访问自己的参数变量,还可以访问把它嵌套其中的父函数的参数和变量

 

 

  (二)”js与其他编程语言不同的时 它是函数作用域,函数内部的参数和变量在函数外面是不可见的,但在函数内部在任意时刻定义的变量,在任意地方都能访问。“

  例

  window.onload = function () {
    for (var i = 0; i<5; i++){
        alert("循环内部"+i);
        }
        alert(i);   //在c中 会显示未定义 循环结束自动消除变量i 块型作用域
     }

     但是,在js中,没有块型作用域,变量是根据函数的存亡而存亡,所以会一次弹出 0 1 2 3 4 5;

  因此 在函数顶端 最好声明所有需要用的变量

(三)”内部函数拥有比他的外部函数更长的生命周期“

    
引用 Douglas Crockford [1] :

之所以可能通过这种方式在 JavaScript 种实现公有,私有,特权变量正是因为闭包闭包是指在 JavaScript 中,内部函数总是可以访问其所在的外部函数中声明的参数和变量,即使在其外部函数被返回(寿命终结)了之后


需要注意的一点时,内部函数访问的是被创建的内部变量本身,而不是它的拷贝。所以在闭包函数内加入 loop 时要格外注意。另外当然的是,闭包特性也可以用于创建私有函数或方法。

--------
关于为什么在 JavaScript 中闭包的应用都有关键词“return”,引用 JavaScript 秘密花园中的一段话:

闭包是 JavaScript 一个非常重要的特性,这意味着当前作用域总是能够访问外部作用域中的变量。 因为 函数 是 JavaScript 中唯一拥有自身作用域的结构,因此闭包的创建依赖于函数。