javascript做用域,做用域链,[[scope]]属性

对于Javascript程序员来讲,闭包总会让你以为既熟悉又陌生,然而它对于开发人员来讲却很是重要,javascript里的许多设计模式中都用到了闭包,此处以函数做用域为例。javascript

//示例代码
    var a=1;
    function foo(){
        var b=2;
        console.log(a);
        function bar(){
            var c=123;
            console.log(b);
        }
        bar();
    }
    foo();

任何函数定义的时候,都会建立一个[[scope]]属性,这个对象对应的是一个对象的列表,列表中的对象仅能javascript内部访问,无法经过语法访问,用代码能够表示为:
1.函数定义时
在全局环境下定义了一个foo函数,此时foo函数的[[scope]]属性中只包含一个全局对象GO(global object)html

//伪代码
    //js代码默认进入全局执行环境,因此foo在初始时就被定义
    foo.[[scope]]={
        GO:{
        this:window,
        window:{...},
        document:{...},
        a:undefined   //此处是预编译,因此a并无赋值
        ....
        }
    }
   //当进入foo执行环境时,bar函数才被定义
   bar.[[scope]]={
        AO(foo):{
            this:window,
            arguments:[],
            b:undefined
        },
        GO:{
            this:window,
            window:{...},
            document:{...},
            a:1
        }
    }

2.函数被调用时
执行环境
在函数执行时,会建立一个叫作执行环境/执行上下文(execution context)的内部对象
它定义了一个函数执行时的环境
函数每次执行时的执行环境独一无二
屡次调用函数就屡次建立执行环境
而且函数执行完毕后,执行环境就会被销毁
执行环境有本身的做用域链,用于解析标识符java

因此当foo函数被调用的时候,会建立foo执行环境,每一个执行环境对应一个变量对象。首先会创一个它本身的活动对象【Activation Object】(这个对象中包含了this、参数(arguments)、局部变量(包括命名的参数)的定义,固然全局对象是没有arguments的)和一个变量对象的做用域链[[scope chain]],而后,把这个执行环境的[[scope]]按顺序复制到[[scope chain]]里,最后把这个活动对象推入到[[scope chain]]的顶部。这样[[scope chain]]就是一个有序的栈,这样保了对执行环境有权访问的全部变量和对象的有序访问。程序员

//foo函数被调用时
    foo.EC={         //foo的执行环境
      AO:{           //foo的活动对象
        this:window,
        arguments:[],
        b:undefined
        },
      [[scope chain]]:{
            AO:AO,   //推入做用域链顶部的活动对象
            GO:{...} //经过复制foo.[[scope]]获得的全局对象
        }
      ...
    }
    //函数的做用域链
    foo.EC.[[scope chain]]={
        AO:{
            this:window,
            arguments:[],
            b:undefined
        },
        GO:{
            this:window,
            window:{...},
            document:{...},
            a:1
        }
    }
//当bar函数被调用时
    bar.EC={
        AO:{
            this:window,
            arguments:[],
            c:undefined
        },
        [[scope chain]]:{
            AO:AO     //推入做用域链顶部的活动对象
            AO:{...}  //foo活动对象
            GO:{...}  //全局活动对象
        }
    }

3.函数代码执行阶段
var b=2 实际上就是对做用域链AO对象中的b进行赋值,当执行console.log(a)时候,遇到标识符a,就会根据标识符的名称在执行环境(Execution Context)的做用域链中进行搜索。从做用域链的第一个对象(该函数的Activation Object对象)开始,若是没有找到,就搜索做用域链中的下一个对象,如此往复,直到找到了标识符的定义。若是在搜索完做用域中的最后一个对象,也就是全局对象(Global Object)之后也没有找到,则会抛出一个错误,提示undefined。 设计模式

正式因为做用域链的这种关系,咱们就不难理解,为何this和arguments不能经过做用域链向上搜索,由于对this和arguments的搜索在当前执行函数的活动对象就中止了。闭包

以上是我的对于js做用域的理解, 若有错误欢迎讨论,本文未涉及with等改变做用域的行为
参考文章
http://www.cnblogs.com/pigtai...
http://blog.csdn.net/liujie19...
http://www.cnblogs.com/vadar/...
http://blog.csdn.net/q1056843...函数

相关文章
相关标签/搜索