js中执行环境、作用域、作用域链的概述

执行环境、作用域、作用域链
什么是执行环境,作用域链,我看了二本书js权威指南、js高级程序设计,看的我是云里雾里,想死的心都有。。。。。。。
“执行环境定义了变量或函数有权访问的其他数据,决定了它们各自的行为。每个执行环境都有一个与之关联的变量对象(variable object),环境中定义的所有变量和函数都保存在这个对象中。虽然我们编写的代码无法访问这个对象,但解析器在处理数据时会在后台使用它。”书上是这么说的,能看懂嘛,来个例子吧

  1. var color = "blue";
  2.                 function changeColor(){
  3.                 if (color === "blue"){
  4.                 color = "red";
  5.                 } else {
  6.                 color = "blue";
  7.                 }
  8.                 }
  9.                 changeColor();
  10.                 alert("Color is now " + color);



首先我说这段代码拥有二个执行环境(每个函数都有自己的执行环境),一个是全局执行环境window,一个是changeColor函数也是个执行的环境。。。
某个执行环境下的所有代码执行完毕后,该环境被销毁,保存在其中的变量和函数也随之销毁,但是全局执行环境直到应用程序退出时(关闭网页或是浏览器)才会被销毁。

对于作用域链书上这样说的:“当代码在一个环境中执行时,会创建变量对象的一个作用域链(scope chain)。作用域链的用途,是保证对执行环境有权访问的所有变量和函数的有序访问。作用域链的前端,始终都是当前执行的代码所在环境的变量对象。如果这个环境是函数,则将其活动对象(activation object)作为变量对象。活动对象在最开始时只包含一个变量,即arguments 对象(这个对象在全局环境中是不存在的)。作用域链中的下一个变量对象来自包含(外部)环境,而再下一个变量对象则来自下一个包含环境。这样,一直延续到全局执行环境;全局执行环境的变量对象始终都是作用域链中的最后一个对象。”
我们来简化一下,但是不一定那么准确:
作用域链:由于js的变量都是对象的属性,而该对象可能又是其它对象的属性,而所有的对象都是window对象的属性,所以这些对象的关系可以看作是一条链,链头就是变量所处的对象,链尾就是window对象

  1. function t() { 
  2.                         var a; 
  3.                         function t2() { 
  4.                                 var b; 
  5.                         } 
  6.                 }
复制代码


js中函数也是对象,所以变量a所在的对象是t,t又在window对象中,所以a的作用域链如下  t--window 

那么b所以在的对象即t2,t2又包含在t中,t又在window对象,所以b的作用域链如下 t2--t--window

关于作用域:变量在声明它们的函数体以及这个函数体嵌套的任意函数体内都是有定义的。
换个容易理解的吧:一个变量的作用域是源程序代码中定义的这个变量的区域。全局变量拥有全局作用域,在js代码中的任何地方都有定义的。然而在函数内部的变量只有函数体内有定义,它们是局部变量,作用域是局部性的。函数的参数也是局部变量,它们只有在函数体内有定义。
例子:

  1. var scope="global";  
  2.                 function t(){  
  3.                     console.log(scope);  //underfined
  4.                     var scope="local"  
  5.                     console.log(scope);  //local
  6.                 }  
  7.                 t();
复制代码


这里的t环境中,变量scope并不是全局变量中的scope,在作用于累变量在声明之前升值已经可用,这个特性称为声明提前。改一下上面的代码:

  1. var scope="global";  
  2.                 function t(){
  3.                         var scope;  
  4.                     console.log(scope);  //underfined
  5.                     var scope="local"  
  6.                     console.log(scope);  //local
  7.                 }  
  8.                 t();
复制代码


可以看出来函数内声明的所有变量(但不涉及赋值)都被提至函数的顶部。
注意:在函数体内局部变量的优先级高于同名的全局变量,如果在函数内声明的一个局部变量或是函数参数中带有的变量和全局变量重名,那么全局变量就会被局部变量所遮盖。

根据作用域链t-window 先在t中找变量scope,找到了就是undefined,当到window时,发现同名,根据优先级,还是选作用域链前面的变量。。。所以是underfined

再看一个作用域的:

  1. window.onload=function(){
  2.                         var scope="global scope";  
  3.                         function checkscope(){
  4.                                 var scope='local scope';
  5.                                 function nested(){
  6.                                         var scope='nested scope'
  7.                                         return scope;
  8.                                 };
  9.                                 return nested();
  10.                         }  
  11.                         var a=checkscope();        
  12.                         console.log(a); //nested scope
  13.                 };


根据作用域链 nested->checkscope->window 很显然作用域链的首部定义的变量scope优先级最高

总结一下,直接copy书上的,写的不错:

  1. var color = "blue";
  2.                 function changeColor(){
  3.                         var anotherColor = "red";
  4.                         function swapColors(){
  5.                                 var tempColor = anotherColor;
  6.                                 anotherColor = color;
  7.                                 color = tempColor;
  8.                                 // 这里可以访问color、anotherColor 和tempColor
  9.                         }
  10.                         // 这里可以访问color 和anotherColor,但不能访问tempColor
  11.                         swapColors();
  12.                 }
  13.                 // 这里只能访问color
  14.                 changeColor();


以上代码共涉及3 个执行环境:全局环境、changeColor()的局部环境和swapColors()的局部环境。全局环境中有一个变量color 和一个函数changeColor()。changeColor()的局部环境中有
一个名为anotherColor 的变量和一个名为swapColors()的函数,但它也可以访问全局环境中的变量color。swapColors()的局部环境中有一个变量tempColor,该变量只能在这个环境中访问到。
无论全局环境还是changeColor()的局部环境都无权访问tempColor。然而,在swapColors()内部则可以访问其他两个环境中的所有变量,因为那两个环境是它的父执行环境。

其中,内部环境可以通过作用域链访问所有的外部环境,但外部环境不能访问内部环境中的任何变量和函数。这些环境之间的联系是线性、有次序的。每个环境都可以向上搜索作用域链,以查询变量和函数名;但任何环境都不能通过向下搜索作用域链而进入另一个执行环境。对于这个例子中的swapColors()而言,其作用域链中包含3个对象:swapColors()的变量对象、changeColor()的变量对象和全局变量对象。swapColors()的局部环境开始时会先在自己的变量对象中搜索变量和函数名,如果搜索不到则再搜索上一级作用域链。changeColor()的作用域链中只包含两个对象:它自己的变量对象和全局变量对象。这也就是说,它不能访问swapColors()的环境。

posted @ 2015-04-14 10:27  angus_jiang  阅读(261)  评论(0编辑  收藏  举报