理解Js作用域和作用域链

来源:互联网 发布:线切割手动圆形编程 编辑:程序博客网 时间:2024/05/23 00:55

一、JavaScript作用域

任何程序设计语言都有作用域的概念,简单地说,作用域就是变量与函数可访问的范围,即作用域控制着变量与函数的可见性和生命周期。在Js中,变量的作用域分为全局作用域和局部作用域,相应的,变量分为全局变量和局部变量。

1、全局作用域
在代码中任何地方都可以访问到的对象拥有全局作用域。一般来说有以下几种情况:

1)、所有window对象拥有全局作用域
一般情况下,window对象的内置属性都拥有全局作用域,例如:window.name,window.top等。

2)、最外层函数以及最外层函数外面定义的变量(全局变量)拥有全局作用域。例如:

var people="xiaoming";function action(){    var a="dance";    function inner(){        alert(a);    }    inner();}alert(name);//xiaoming,name为全局变量alert(a);//错误,a为局部变量action();//danceinner()//错误

3)、所有未定义直接赋值的变量都是全局变量

function action(){    var a="dance";    b="sing";}alert(a);//错误,a为局部变量alert(b);//sing,b为全局变量

2、局部作用域
与全局作用域相反,局部作用域一般只在固定代码片段可访问到。最常见的例如函数内部声明的变量。例如下列代码中的变量a和函数inner都只拥有局部作用域。

var people="xiaoming";function action(){    var a="dance";    function inner(){        alert(a);    }    inner();}alert(name);//xiaoming,name为全局变量alert(a);//错误,a为局部变量action();//danceinner()//错误

3、补充说明:在Js中,没有块级作用域的概念。
如上所述,在Js中,没有块级作用域的概念。这意味着在块语句中定义的变量,实际上是包含在函数中而非语句中创建的。来看下面的例子。

function out(){    for(i=0;i<3;i++){        alert(i);//0,1,2    }    alert(i);//3}out();//0,1,2,3

函数定义了一个for循环,变量i的值被初始化为0。在C++等其他语言中,变量i只会在for循环中有定义,循环一旦结束,变量i就会被销毁。然而在JavaScript中,变量i是定义在函数out()的活动对象中,因而从它有定义开始,就可以在函数内部随处访问它。

那么如何解决这一问题呢?我们可以用匿名函数来模仿块级作用域以避免上述问题。如下:

(function(){    //这里是块级作用域})();

此时,上面的例子则会变为:

function out(){    (function(){        for(i=0;i<3;i++){            alert(i);//0,1,2        }    })();    alert(i);//导致一个错误!}

二、作用域链

当代码在一个环境中执行时,会创建变量对象(保存了该环境中定义所有变量和函数)的一个作用域链。作用域链的用途,是保证对执行环境有权访问的所有变量和函数的有序访问

作用域链的前端,始终都是当前执行的代码所在环境的变量对象。如果这个环境是函数,则将其活动对象(活动对象最开始时只包含一个变量,即arguments对象(这个对象在全局环境中是不存在的))作为变量对象。作用域链中的下一个变量对象来自包含环境(当前环境的父辈环境),而再下一个变量对象则来自下一个包含环境。这样,一直延续到全局执行环境。全局执行环境的变量对象始终都是作用域链的最后一个对象。

光看文字也许你有点晕,让我们来看一个简单的例子。

var color="blue";function changeColor(){    var anotherColor="red";    function swapColor(){        var tempColor=anotherColor;        anotherColor=color;        color=tempColor;        //在这里可以访问到color,anotherColor,tempColor    }    //在这里可以访问到color,anotherColor}//在这里只访问到colorchangeColor();

上面共涉及到三个执行环境:全局环境,changeColor()局部环境以及swapColor()局部环境。
全局变量环境中有一个变量color和一个函数changeColor()。changeColor()局部环境中有一个变量anotherColor和一个函数swapColor()。但它也可以访问到全局环境中的color变量。swapColor()局部环境中有一个变量tempColor,该变量只可以在这个环境中访问到。然而在swapColor()内部则可以访问其他两个环境中的所有变量,因为那两个环境是它的父执行环境。

总而言之,内部环境可以通过作用域链访问所有的外部环境,但外部环境不能访问内部环境中的任何变量和函数。

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

原创粉丝点击