JavaScript中的做用域链和闭包

JavaScript中的做用域链和闭包

    JavaScript中出现了一个之前没学过的概念——闭包。何为闭包?从表面理解即封闭的包,与做用域有关。因此,说闭包之前先说说做用域。javascript

做用域(scope)html

    一般来讲一段程序代码中使用的变量和函数并不老是可用的,限定其可用性的范围即做用域,做用域的使用提升了程序逻辑的局部性,加强程序的可靠性,减小名字冲突。前端

    全局做用域(Global Scope)java

    在代码中任何地方都能访问到的对象拥有全局做用域,如下几种情形拥有全局做用域:编程

    一、最外层函数和在最外层函数外面定义的变量拥有全局做用域,例如:闭包

[javascript]  view plain copy print ?
 
  1. <span style="font-family:KaiTi_GB2312;font-size:18px;">var outSide="var outside";  
  2. function outFunction(){  
  3.     var name="var inside";  
  4.     function inSideFunction(){  
  5.         alert(name);  
  6.     }  
  7.     inSideFunction();  
  8. }  
  9. alert(outSide); //正确  
  10. alert(name); //错误  
  11. outFunction(); //正确  
  12. inSideFunction() //错误</span>  

    二、未定义直接赋值的变量自动声明为拥有全局做用域,例如:ide

 

[javascript]  view plain copy print ?
 
  1. <span style="font-family:KaiTi_GB2312;font-size:18px;">blogName="CSDN李达"</span>  
    三、全部window对象的属性拥有全局做用域,例如:window对象的内置属性都拥有全局做用域,例如window.name、window.location、window.top等

 

局部做用域(Local Scope)函数

[javascript]  view plain copy print ?
 
  1. <span style="font-family:KaiTi_GB2312;font-size:18px;">function outFunction(){  
  2.          var name="inside name";  
  3.         function inFunction(){  
  4.                 alert(name);  
  5.          }  
  6.          inFunction();  
  7. }  
  8. alert(name); //错误  
  9. inFunction(); //错误</span>  
做用域链(scope chain)

 

    JavaScript中,JavaScript里一切都是对象,包括函数。函数对象和其它对象同样,拥有能够经过代码访问的属性和一系列仅供JavaScript引擎访问的内部属性。其中一个内部属性是做用域,包含了函数被建立的做用域中对象的集合,称为函数的做用域链,它决定了哪些数据能被函数访问。
  当一个函数建立后,它的做用域链会被建立此函数的做用域中可访问的数据对象填充。例如函数:post

[javascript]  view plain copy print ?
 
  1. <span style="font-family:KaiTi_GB2312;font-size:18px;">function add(num1,num2) {  
  2.     var sum = num1 + num2;  
  3.     return sum;  
  4. }</span>  

    在函数add建立时,它的做用域链中会填入一个全局对象,该全局对象包含了全部全局变量,以下图所示(注意:图片只例举了所有变量中的一部分):优化

 

    因而可知,函数的做用域链是建立函数的时候建立的。

执行上下文(Execute context )

    函数add的做用域将会在执行时用到,例如:

 

[javascript]  view plain copy print ?
 
  1. <span style="font-family:KaiTi_GB2312;font-size:18px;">var total = add(5,10);</span>  

 

    当执行 add 函数的时候, JavaScript 会建立一个 Execute context (执行上下文),执行上下文中就包含了 add 函数运行期所须要的全部信息。 Execute context 也有本身的 Scope chain, 当函数运行时, JavaScript 引擎会首先从用 add 函数的做用域链来初始化执行上下文的做用域链。

活动对象(Active Object)

    而后 JavaScript 引擎又会建立一个 Active Object, 这些值按照它们出如今函数中的顺序被复制到运行期上下文的做用域链中,它们共同组成了一个新的对象——“活动对象(activation object)”,这个对象里面包含了函数运行期的全部局部变量,参数以及 this 等变量,此对象会被推入做用域链的前端,当运行期上下文被销毁,活动对象也随之销毁。新的做用域链以下图所示:

    执行上下文是一个动态的概念,当函数运行的时候建立,活动对象 Active Object 也是一个动态的概念,它是被执行上下文的做用域链引用的,能够得出结论:执行上下文和活动对象都是动态概念,而且执行上下文的做用域链是由函数做用域链初始化的。

    在函数执行过程当中,每遇到一个变量,都会检索从哪里获取和存储数据,该过程从做用域链头部,也就是从活动对象开始搜索,查找同名的标识符,若是找到了就使用这个标识符对应的变量,若是没有则继续搜索做用域链中的下一个对象,若是搜索完全部对象都未找到,则认为该标识符未定义,函数执行过程当中,每一个标识符都要经历这样的搜索过程。

闭包(closure)

        先来看一个实例,javascript代码:

[javascript]  view plain copy print ?
 
  1. <span style="font-family:KaiTi_GB2312;font-size:18px;">    <script type="text/javascript">  
  2.     function newLoad(){ //新建页面加载的事件  
  3.         for (var i = 1; i <=3; i++) {  
  4.                 var anchor = document.getElementById("anchor" + i); //找到每一个anchor  
  5.                 anchor.onclick = function () {//为anchor添加单击事件  
  6.                         alert ("you clicked anchor"+i);//给出点击反应  
  7.                 }  
  8.         }  
  9. }  
  10. window.onload = newLoad; //把newload事件赋值给页面加载  
  11.     </script></span>  
    前台代码:
[html]  view plain copy print ?
 
  1. <span style="font-family:KaiTi_GB2312;font-size:18px;"><body>  
  2. <id="anchor1" href="#">anchor1</a><br/>  
  3. <id="anchor2" href="#">anchor2</a><br/>  
  4. <id="anchor3" href="#">anchor3</a><br/>  
  5. </body></span>  
    运行结果:不管点击那个anchor,总会弹出anchor4,而咱们根本就没有anchor4:

 

    当咱们加载页面时,javascript中的newLoad函数已经运行完毕,由其中的循环可知,anchor已经赋值为4。可是由之前的编程经验来看,局部变量使用完毕就会销毁,可是anchor却没有,显然这里 JavaScript 采用了另外的方式。

    闭包在 JavaScript 其实就是一个函数,在函数运行期被建立的,当上面的 函数被执行的时候,会建立一个闭包,而这个闭包会引用newLoad 做用域中的anchor。下面就来看看 JavaScript 是如何来实现闭包的:当执行 newLoad 函数的时候, JavaScript 引擎会建立newLoad函数执行上下文的做用域链,这个做用域链包含了 newLoad执行时的活动对象,同时 JavaScript 引擎也会建立一个闭包,而闭包的做用域链也会引用 newload的活动对象,这样当 newload执行完的时候,虽然其执行上下文和活动对象都已经释放了anchor,可是闭包仍是引用着 newload 的活动对象,因此点击显示的是“you clicked anchor4”。运行期如图:

闭包优化

    既然闭包出现了咱们不想看到的结果,咱们须要优化它。优化后的javascript(其余不变):

[javascript]  view plain copy print ?
 
  1. <span style="font-family:KaiTi_GB2312;font-size:18px;">     <script type="text/javascript">  
  2.             function newLoad() { //新建页面加载的事件  
  3.                     for (var i = 1; i <= 3; i++) {  
  4.                             var anchor = document.getElementById("anchor" + i); //找到每一个anchor  
  5.                             listener(anchor,i);//listener函数  
  6.                             }  
  7.                     }  
  8.                 function listener(anchor, i) {  
  9.                         anchor.onclick = function () {//为anchor添加单击事件  
  10.                                 alert("you clicked anchor" + i); //给出点击反应  
  11.                         }  
  12.                }  
  13.             window.onload = newLoad; //把newload事件赋值给页面加载  
  14.     </script></span>  
    运行结果:提示对应的提示信息

 

    

    结果分析:优化后的结果是由于,咱们把声明的变量和单击事件相分离。用以上解释的做用域链解释:页面加载,先执行listener函数,而listener函数须要anchor变量,在listener函数做用域链中没有,会进入下一级做用域链,即查找newLoad中的anchor,由于在listener中已经肯定了哪一个anchor单击对应哪一个提示信息,因此只是在newload中查找对应的anchor而已,不会等循环完毕产生anchor4。

    由于接触javascript时间尚短,理解有误的地方欢迎指正。

 1:【闭包在 JavaScript 其实就是一个函数,在函数运行期被建立的,当上面的 函数被执行的时候,会建立一个闭包,而这个闭包会引用newLoad 做用域中的anchor。】
应该是:当上面的 函数被执行的时候,会建立一个闭包,而这个闭包会引用newLoad 做用域中的i
2:【结果分析:优化后的结果是由于,咱们把声明的变量和单击事件相分离。用以上解释的做用域链解释:页面加载,先执行listener函数,而listener函数须要anchor变量,在listener函数做用域链中没有,会进入下一级做用域链,即查找newLoad中的anchor,由于在listener中已经肯定了哪一个anchor单击对应哪一个提示信息,因此只是在newload中查找对应的anchor而已,不会等循环完毕产生anchor4。】应该是:listener(anchor,i);函数3次执行会产生3个不一样的闭包,分别引用不一样的anchor 和i。因此执行 anchor.onclick = function () { alert("you clicked anchor" + i);} 时会根据3个不一样的"执行上下文"的做用域链而找到不一样的i变量。
3:总结:子函数会根据拥有当初时刻"执行上下文对象"的做用域链的闭包而找到当初时刻的存储的一系类值。
4:闭包算是执行时动态的分配并保持对当时状态的记忆存储。
5:函数每次执行时对应的运行期上下文都是独一无二的,因此屡次调用同一个函数就会致使建立多个运行期上下文,当函数执行完毕,执行上下文会被销毁。每个运行期上下文都和一个做用域链关联。
相关文章
相关标签/搜索