深深明JavaScript的闭包特性如何吃循环中的对象上加波

初学者经常碰到的,即获取HTML元素集合,循环给元素添加事件。在事件响应函数中(event handler)获取对应的索引。但每次获取的都是最后一次循环的索引。原因是初学者并未理解JavaScript的闭包特性。

有个网友问了个问题,如下的html,为什么点击所有的段落p输出都是5,而不是alert出对应的0,1,2,3,4。

1.  <!DOCTYPE HTML>

2.  <html>

3.  <head>

4.  <meta charset="utf-8" />

5.  <title>闭包演示</title>

6.  <style type="text/css">

7.      p {background:gold;}

8.  </style>

9.  <script type="text/javascript">

10.function init() {

11.    var pAry = document.getElementsByTagName("p");

12.    for( var i=0; i<pAry.length; i++ ) {

13.         pAry[i].onclick = function() {

14.         alert(i);

15.    }

16.  }

17.}

18.</script>

19.</head>

20.<body onload="init();">

21.<p>产品 0</p>

22.<p>产品 1</p>

23.<p>产品 2</p>

24.<p>产品 3</p>

25.<p>产品 4</p>

26.</body>

27.</html>

以上场景是初学者经常碰到的。即获取HTML元素集合,循环给元素添加事件。在事件响应函数中(event handler)获取对应的索引。但每次获取的都是最后一次循环的索引。

原因是初学者并未理解JavaScript的闭包特性。通过element.onclick=function(){alert(i);}方式给元 素添加点击事件。响应函数function(){alert(i);}中的 i 并非每次循环时对应的 i(如0,1,2,3,4)而是循环后最后 i 的值5。 或者说循环时响应函数内并未能保存对应的值 i,而是最后一次i++的值5。

了解了原因,下面就由几种方式可与解决:

1、将变量 i 保存给在每个段落对象(p)上

1.  function init1() {

2.    var pAry = document.getElementsByTagName("p");

3.    for( var i=0; i<pAry.length; i++ ) {

4.       pAry[i].i = i;

5.       pAry[i].onclick = function() {

6.          alert(this.i);

7.       }

8.    }

9.  }

2、将变量 i 保存在匿名函数自身

1.  function init2() {

2.    var pAry = document.getElementsByTagName("p");

3.    for( var i=0; i<pAry.length; i++ ) {

4.     (pAry[i].onclick = function() {

5.          alert(arguments.callee.i);

6.      }).i = i;

7.    }

8.  }

3、加一层闭包,i 以函数参数形式传递给内层函数

1.  function init3() {

2.    var pAry = document.getElementsByTagName("p");

3.    for( var i=0; i<pAry.length; i++ ) {

4.     (function(arg){

5.         pAry[i].onclick = function() {

6.            alert(arg);

7.         };

8.     })(i);//调用时参数

9.    }

10.}

4、加一层闭包,i 以局部变量形式传递给内层函数

1.  function init4() {

2.    var pAry = document.getElementsByTagName("p");

3.    for( var i=0; i<pAry.length; i++ ) {

4.      (function () {

5.        var temp = i;//调用时局部变量

6.        pAry[i].onclick = function() {

7.          alert(temp);

8.        }

9.      })();

10.  }

11.}

5、加一层闭包,返回一个函数作为响应事件(注意与3的细微区别)

1.  function init5() {

2.    var pAry = document.getElementsByTagName("p");

3.    for( var i=0; i<pAry.length; i++ ) {

4.     pAry[i].onclick = function(arg) {

5.         return function() {//返回一个函数

6.         alert(arg);

7.       }

8.     }(i);

9.    }

10.}

6、用Function实现,实际上每产生一个函数实例就会产生一个闭包

1.  function init6() {

2.      var pAry = document.getElementsByTagName("p");

3.      for( var i=0; i<pAry.length; i++ ) {

4.        pAry[i].onclick = new Function("alert(" + i + ");");//new一次就产生一个函数实例

5.      }

6.  }

7、用Function实现,注意与6的区别

1.  function init7() {

2.      var pAry = document.getElementsByTagName("p");

3.      for( var i=0; i<pAry.length; i++ ) {

4.           pAry[i].onclick = Function(‘alert(‘+i+’)’);

5.      }

6.  }

 

 

浅析Javascript闭包的特色

2009-07-24 17:30 司徒正美
cnblogs 自一旦评论(1) 字号:T | T

 

本文将本着Javascript闭包的特点开展解析,并举例进行验证。闭包,是指语法域位于有特定的区域,具有持续参照(读写)位于该区域外己范围之外的执行域上的非持久型变量值能力的截。

AD:

Javascript闭包的概念格外晦涩——闭包,是指语法域位于有特定的区域,具有持续参照(读写)位于该区域外己范围以外的执行域上之非持久型变量值能力的截。这些外部执行域的非持久型变量神奇地保留其当闭包最初定义(或创造)时之值(深连结)。

简单的话,Javascript闭包就是是在其它一个作用域中保留了一致客它从上一级函数或作用域取得的变量(键值对),而这些键值对凡休会见按上一级函数的推行得而销毁。周爱民说得又明白,闭包就是“属性表”,闭包就是一个数据块,闭包就是一个存着“Name=Value”的对照表。就如此简单。但是,必须强调,闭包是运行期概念,一个函数实例。

Javascript闭包的兑现,通常是当函数内部又定义函数,让该内部函数使用及一级函数的变量或全局变量。

ECMAScript看以全局变量是一个略的Javascript闭包实例。

1.  var sMessage = “Hello World”;   

2.  function sayHelloWorld(){   

3.  alert(sMessage);   

4.  };   

5.  sayHelloWorld(); 

唯独它们完成无体现Javascript闭包的特征……

现行较给丁肯定的Javascript闭包实现有如下三种植

1.  with(obj){   

2.  //这里是目标闭包   

3.  }(function(){      

4.  //函数闭包   

5.  })()try{   

6.  //…   

7.  } catch(e) {   

8.  //catch闭包 但IE里不行   

9.  } 

附上今天当无忧看到的题目:

要求:

让这三单节点的Onclick事件都能对的弹奏有相应的参数。

1.  <ul>    

2.  <li id=”a1″>aa</li>    

3.  <li id=”a2″>aa</li>   

4.  <li id=”a3″>aa</li>   

5.  </ul>   

6.  <script type=”text/javascript”>   

7.  <ul>   

8.  <li id=”a1″>aa</li>   

9.  <li id=”a2″>aa</li>   

10. <li id=”a3″>aa</li>   

11. </ul>   

12. <script type=”text/javascript”>   

  1. for(var i=1; i < 4; i++){   

  2. var id = document.getElementById(“a” + i);   

  3. id.onclick = function(){   

  4. alert(i);//现在还是回4      

  5. }   

  6. }   

19. </script> 

客服果果的解答:

1.  for(var i=1; i < 4; i++){      

2.  var id = document.getElementById(“a” + i);     

3.  /*     

4.  这里非常成了一个匿名函数并赋值给目标 id_i;     

5.  */     

6.  id.onclick = function(){          

7.  /*          

8. 
此i来源于局部变量,无法以window.i或者obj.i的款型在深引用,          

9.  不得不坐指针或者变量地址方式保留于此匿名函数中,          

  1. 当时虽是风传的闭包,所以有这个过程中生成的风波句柄都使用引用        
     

  2. 的点子来始终如一这个变量,也就算是这些匿名函数共用一个变量i;          

  3. */         

  4. alert(i);      

  5. };   

  6. }; 

一部分变全局

1.  for(var i=1; i < 4; i++){   

2.  var id = document.getElementById(“a” + i);     

3.  id.i=i;//这个i有了根     

4.  id.onclick=function(){          

5.  alert(this.i)      

6.  };   

7.  };1.for(var i=1; i < 4; i++){     

8.  var id = document.getElementById(“a” + i);    

9.  window[id.id]=i;//这个i有了根    

  1. id.onclick=function(){         

  2. alert(window[this.id]);     

  3. };   

出一定之重新多Javascript闭包

1.  for(var i=1; i < 4; i++){    

2.  var id = document.getElementById(“a” + i);    

3.  id.onclick = new function(){        

4.  var i2=i;//这个i是闭包的闭包       

5.  return function(){           

6.  alert(i2);       

7.  }     

8.  };   

9.  } 

javascript深入理解js闭包发布:dxy 字体:[增加 减小] 类型:转载

 

闭包(closure)是Javascript语言的一个难点,也是她的特色,很多高档应用还使借助闭包实现。

一如既往、变量的作用域

 

只要清楚闭包,首先须掌握Javascript特殊的变量作用域。

 

变量的作用域无非就是是少栽:全局变量和部分变量。

 

Javascript语言的新鲜的处在,就在函数内部可以一直读取全局变量。

 

 

Js代码

 

  var n=999;

 

  function f1(){

    alert(n);

  }

 

  f1(); // 999

 

另一方面,在函数外部自然无法读取函数内之片段变量。

 

Js代码

 

  function f1(){

    var n=999;

  }

 

  alert(n); // error

 

此出一个地方要留意,函数内部宣称变量的时候,一定要以var命令。如果未用底讲话,你实际声明了一个全局变量!

 

Js代码

 

  function f1(){

    n=999;

  }

 

  f1();

 

  alert(n); // 999

 


 

亚、如何自外表读取局部变量?

 

鉴于种种原因,我们有时要得到函数内之片段变量。但是,前面都说过了,正常状态下,这是惩治不顶的,只有经过变通方法才会兑现。

 

那么就是是当函数的里,再定义一个函数。

 

Js代码

 

  function f1(){

 

    n=999;

 

    function f2(){

      alert(n); // 999

    }

 

  }

 

当点的代码中,函数f2即使为概括在部数f1之中,这时f1之中的具有片段变量,对f2都是可见的。但是转头就死,f2里边的一些变量,对f1
就是不可见的。这就算是Javascript语言特有的“链式作用域”结构(chain
scope),

 

支行对象会一级一级地于达搜寻有父对象的变量。所以,父对象的兼具变量,对子对象还是可见的,反的则非树立。

 

既然f2可以读取f1遭遇的组成部分变量,那么一旦将f2作为返回值,我们无就足以当f1外部读取它的其中变量了吗!

 

 

Js代码

 

  function f1(){

 

    n=999;

 

    function f2(){

      alert(n);

    }

 

    return f2;

 

  }

 

  var result=f1();

 

  result(); // 999

 


 

老三、闭包的定义

 

落得同样节约代码中之f2函数,就是闭包。

 

各种规范文献达到的“闭包”(closure)定义格外抽象,很难看懂。我的懂得是,闭包就是会读取其他函数内部变量的函数。

 

由当Javascript语言中,只有函数内部的子函数才会念博有变量,因此可以把闭包简单了解成“定义在一个函数内部的函数”。

 

所以,在真相上,闭包就是以函数内部与函数外部连接起来的一模一样座大桥。

 

——————————————————————————————————–b

 

季、闭包的用处

 

闭包可以据此当许多地方。它的最为要命从而处来有限个,一个凡眼前提到的好读取函数内部的变量,另一个就是为这些变量的价老维持以内存中。

 

岂来了解当下句话也?请圈下面的代码。

 

 

Js代码

 

  function f1(){

 

    var n=999;

 

    nAdd=function(){n+=1}

 

    function f2(){

      alert(n);

    }

 

    return f2;

 

  }

 

  var result=f1();

 

  result(); // 999

 

  nAdd();

 

  result(); // 1000

 

于这段代码中,result实际上即便是闭包f2函数。它一起运行了区区次,第一次的值是999,第二软的价值是1000。这说明了,函数f1吃的一对变量n一直保留在内存中,并无以f1调整用后受自动清除。

 

何以会这么为?原因就是在于f1是f2的父函数,而f2于与给了一个全局变量,这招f2始终当内存中,而f2的有依靠让f1,因此f1也总在内存中,不会见当调用了后,被垃圾回收机制(garbage
collection)回收。

 

即段代码中任何一个值得注意的地方,就是“nAdd=function(){n+=1}”这无异实施,首先以nAdd前面没有采用var关键字,因此
nAdd是一个全局变量,而非是有变量。其次,nAdd的价是一个匿名函数(anonymous
function),而之

 

匿名函数本身也是一个闭包,所以nAdd相当给是一个setter,可以在函数外部对函数内部的有的变量进行操作。

 


 

五、使用闭包的顾点

 

1)由于闭包会使得函数中之变量都吃封存在内存中,内存消耗大怪,所以未可知滥用闭包,否则会招致网页的性能问题,在IE中恐造成内存泄露。解决措施是,在脱离函数之前,将不以的有变量全部去除。

 

2)闭包会在父函数外部,改变父函数间变量的价值。所以,如果你拿父函数当作对象(object)使用,把闭包当作它的公用方法(Public
Method),把里面变量当作它的民用属性(private
value),这时一定要是小心,不要管

 

更改父函数里面变量的价值。

 


 

六、思考题

 

要是您会了解下面代码的周转结果,应该就知道闭包的运行机制了。

 

Js代码

  var name = “The Window”;

  var object = {

    name : “My Object”,

    getNameFunc : function(){

      return function(){

        return this.name;

     };

    }

};

alert(object.getNameFunc()()); //The Window

 


JavaScript闭包例子

 

function outerFun()

{

var a=0;

function innerFun()

{

a++;

alert(a);

}

}

innerFun()

 

方的代码是误的.innerFun()的作用域在outerFun()内部,所在outerFun()外部调用它是不当的.

 

变动成为如下,也就是是闭包:

 

Js代码

 

function outerFun()

{

var a=0;

function innerFun()

{

a++;

alert(a);

}

return innerFun; //注意这里

}

var obj=outerFun();

obj(); //结果为1

obj(); //结果为2

var obj2=outerFun();

obj2(); //结果为1

obj2(); //结果为2

 

嗬是闭包:

 

当其中函数 在概念其的意图域 的外部 被引用时,就创造了拖欠内部函数的闭包
,如果中间函数引用了在外部函数的变量,当外部函数调用完毕后,这些变量在内存不会见于
释放,因为闭包需要它们.

 


 

双重来拘禁一个例证

 

Js代码

 

function outerFun()

{

var a =0;

alert(a);

}

var a=4;

outerFun();

alert(a);

 

结果是 0,4 . 因于函数内部采用了var关键字 维护a的作用域在outFun()内部.

 

更看下面的代码:

 

Js代码

 

function outerFun()

{

//没有var

a =0;

alert(a);

}

var a=4;

outerFun();

alert(a);

结果吗 0,0 真是想不到,为什么呢?

 

企图域链是叙一栽途径的术语,沿着该路线可以确定变量的价值
.当执行a=0时,因为没采用var关键字,因此赋值操作会沿着作用域链到var a=4;
并改变其值.

 


 

 

苟你针对javascript闭包还免是怪懂,那么请看下转载的文章:(转载:http://www.felixwoo.com/archives/247)

 

 

 

一如既往、什么是闭包?

 

官方”的解释是:闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是欠表达式的一律有些。

信任那个少有人会一直看明白就句话,因为他叙述的极端学术。其实这词话通俗的吧就是:JavaScript中所有的function都是一个闭包。不过貌似的话,嵌套的function所发出的闭包更为有力,也是多数上我们所谓的“闭包”。看下面这段代码:

 

function a() {  var i = 0;  function b() { alert(++i); }  return b;}var
c = a();c();这段代码有零星独特性:

 

1、函数b嵌套在函数a内部;

 

2、函数a返回函数b。

 

援关系要图:

 

 

 

  这样于推行完var
c=a()后,变量c实际上是依为了函数b,再执行c()后哪怕见面弹来一个窗口显示i的价(第一不成也1)。这段代码其实就创办了一个闭包,为什么?因为函数a外之变量c引用了函数a内之函数b,就是说:

 

  当函数a的内部函数b被函数a外的一个变量引用的时段,就创造了一个闭包。

 

 

  让咱说之再度淋漓一些。所谓“闭包”,就是在构造函数体内定义另外的函数作为目标对象的计函数,而以此目标的措施函数反过来引用外层函数体中的临时变量。这让只要目标
对象在生存期内总会保障其方式,就可知间接保持原来构造函数体立运的临时变量值。尽管最开头之构造函数调用已经终结,临时变量的称为还石沉大海了,但每当目
标对象的主意外也一直会引用到该变量的价值,而且该值只会联网这种措施来做客。即使再调用相同之构造函数,但不过会生成新对象以及方,新的临时变量只是对诺新
的价,和上次那么次调用的是独家独立的。

 

其次、闭包有什么打算?

 

 

  简而言之,闭包的企图就是是于a执行了并赶回后,闭包使得Javascript的垃圾回收机制GC不会见收回a所占用的资源,因为a的中函数b的实践要负a中之变量。这是对闭包作用的不胜直接的描述,不专业也未谨言慎行,但大体意思就是是这么,理解闭包需要循序渐进的进程。

 

以上头的例证中,由于闭包的是叫函数a返回后,a中之i始终存在,这样每次执行c(),i都是自加1后alert出i底值。

 

  那
么我们来设想另一样种状态,如果a返回的匪是函数b,情况就了不同了。因为a执行了晚,b没有吃归给a的之外,只是吃a所引述,而此时a也惟有见面给b引
用,因此函数a和b互相引用但还要休深受外界打扰(被外引用),函数a和b就会见叫GC回收。(关于Javascript的废品回收机制以当后边详细介绍)

 

其三、闭包内的微观世界

 

  如果要是尤其深切的摸底闭包以及函数a和嵌套函数b的涉,我们要引入另外几独概念:函数的实行环境(excution
context)、活动目标(call object)、作用域(scope)、作用域链(scope
chain)。以函数a从概念及实施之历程为条例阐述这几乎独概念。

 

当定义函数a的上,js解释器会将函数a的打算域链(scope
chain)设置为定义a时a所当的“环境”,如果a是一个大局函数,则scope
chain中只有window对象。

当尽函数a的时刻,a会进去相应的执行环境(excution context)。

在创立行环境的进程遭到,首先会否a添加一个scope属性,即a的作用域,其值就为第1步着之scope
chain。即a.scope=a的用意域链。

下一场实施环境会创建一个运动目标(call
object)。活动对象为是一个有着属性之靶子,但它们不有原型而且不能够经过JavaScript代码直接看。创建了运动对象后,把活动对象上加到a的作用域链的极端上方。此时a的意域链包含了点滴单目标:a的运动目标同window对象。

生一样步是在活动对象上补偿加一个arguments属性,它保存着调用函数a时所传递的参数。

末了将拥有函数a的形参和内部的函数b的援也加加到a的位移目标及。在就无异步着,完成了函数b的底概念,因此若第3步,函数b的打算域链被设置也b所让定义之条件,即a的作用域。

交者,整个函数a从概念及执行的步骤就是完了了。此时a返回函数b的援给c,又函数b的企图域链包含了针对函数a的移位目标的援,也就是说b可以拜到a中定义之有着变量和函数。函数b被c引用,函数b又依赖函数a,因此函数a在回来后未会见为GC回收。

 

当函数b执行的时候也会像上述步骤一样。因此,执行时b的打算域链包含了3只目标:b的运动目标、a的活动对象以及window对象,如下图所示:

 

 

 

如图所示,当在函数b中做客一个变量的上,搜索顺序是:

 

先找找自身的移动对象,如果在则归,如果非在以延续搜索函数a的活动对象,依次查找,直到找到了。

设若函数b存在prototype原型对象,则当搜寻了我的移位目标后先物色自身之原型对象,再累寻找。这虽是Javascript中之变量查找体制。

假使全作用域链上都没法儿找到,则返回undefined。

小结,本段中涉及了少于单关键的词语:函数的概念跟执行。文中提到函数的作用域是当概念函数时候便已确定,而休是以实施的时候确定(参看步骤1及3)。用同段代码来验证这个题材:

 

function f(x) {   var g = function () { return x; }  return g;}var h =
f(1);alert(h()); 这段代码中变量h指向了f中之不行匿名函数(由g返回)。

 

假设函数h的作用域是以执行alert(h())确定的,那么此时h的意域链是:h的移动目标->alert的运动目标->window对象。

假设函数h的作用域是于概念时规定的,就是说h指向的大匿名函数在概念之时段便早已规定了作用域。那么在实行的时节,h的意域链为:h的活动对象->f的活动目标->window对象。

要第一栽要成立,那输出值就是undefined;如果第二种植而成立,输出值则也1。

 

运作结果证实了第2只假设是对的,说明函数的作用域确实是以概念之函数的时节即便曾经规定了。

 

 

 

季、闭包的以场景

保安函数内的变量安全。以最开头之例证也例,函数a中i只出函数b才能够访问,而一筹莫展透过任何路线看到,因此护了i的安全性。

 

当内存中维系一个变量。依然要前例,由于闭包,函数a中i的直白留存被外存中,因此老是执行c(),都见面让i自加1。

经维护变量的安全实现JS私有属性和私家方法(不可知吃表面看)

私属性与办法以Constructor外是无能为力被拜的

function Constructor(…) {

var that = this;

var membername = value;

function membername(…) {…}

}

 

以上3点凡是闭包最核心的利用场景,很多藏案例都出自这。

 

 

 

五、Javascript的排泄物回收机制

 

 

在Javascript中,如果一个靶不再为引用,那么这个目标就会见为GC回收。如果简单单对象互相引用,而不再受第3者所引述,那么就片独相引用的靶子也会受回收。因为函数a被b引用,b又为a外之c引用,这就是是为何函数a执行后无见面让回收的原委。

 

 

六、结语

 

喻JavaScript的闭包是迈向高级JS程序员的必经之路,理解了那解释与运行机制才会写有逾安全以及优雅的代码。