理解做用域和做用域链

做用域须要引用执行上下文变量对象的概念,咱们先简单讲下。html

在ECMASscript中的代码有三种类型:global, function和eval。咱们的代码在执行过程当中会造成执行上下文,一个执行上下文能够激活另外一个上下文,就比如一个函数调用了另外一个函数(或者全局的上下文调用了一个全局函数),而后一层一层调用下去。逻辑上来讲,这种实现方式是栈,咱们能够称之为上下文堆栈。当一段程序开始时,会先进入全局执行上下文环境[global execution context], 这个也是堆栈中最底部的元素。此全局程序会初始化生成必要的对象[objects]和函数[functions]。 在全局上下文执行的过程当中,它可能会激活一些方法(已经初始化过的),而后进入他们的上下文环境并将新的元素压入堆栈。在这些初始化都结束以后,这个系统会因事件触发一些方法,而后进入一个新的上下文环境。活动的执行上下文在逻辑上组成堆栈,这些堆栈咱们能够当作是一个待编译队列,它的活动或执行顺序影响着机器最终编译修改的结果是否是咱们想要的结果相同。前端

变量对象(VO)是一个与执行上下文相关的特殊对象,它存储着在上下文中声明的·变量(var 变量声明)和·函数声明(FD)。变量对象在每次进入上下文时建立,并填入初始值,值的更新出如今代码执行阶段。闭包

执行上下文的代码被分红两个基本的阶段来处理:1.进入执行上下文;2.执行代码。变量对象的修改变化与这两个阶段紧密相关。ecmascript

做用域指的是变量的适用范围,即在程序的执行上下文(可执行代码)中变量的可访问性。根据变量可访问范围做用域有全局做用域和局部做用域两种类型,在EcmaScript中局部变量只能经过“函数(function)”代码类型的执行上下文建立。在函数内部定义的变量与内部函数,在外部非直接可见而且不污染全局对象。函数

全局做用域的变量对象能够在其它全部上下文环境中访问,拥有全局对象的变量包括:一、全局上下文中的函数和变量。二、未经定义直接赋值的声明。三、window对象的全部属性。this

注:当声明一个全局变量的时候,其实是定义了全局对象window的一个属性。spa

var a = 1;
console.log(window.a);    //1

b = 2;
console.log(window.b);    //2

在ecmascript(ES6以前)中没有块级做用域,只有在函数中有局部做用域,且其变量在外部不可访问。code

for(a=0;a<4;a++){
    
}

var b = 2;
function fun(){
    var c = 8;
    if(!b){
        var b = 6;
    }
    console.log(b);    
}
fun();    //6
console.log(a);    //4
console.log(b);    //2
console.log(c);    //报错 c is not defined

做用域链orm

在EcmaScript中没有静态做用域(没有私有属性和方法),不过它能够给构造函数即函数对象提供属性和方法,其中一个内部属性[[Scope]]属性 -- 它包含了函数内部上下文全部变量对象(包括父变量对象)的集合,这个集合被称为函数的做用域链。此链用来变量查询。例如,当一个函数在自身函数体内须要引用一个变量,可是这个变量并无在函数内部声明(或者也不是某个参数名),那么这个变量就能够称为自由变量[free variable]。那么咱们搜寻这些自由变量就须要用到做用域链。函数上下文的做用域链在函数调用时建立的,包含活动对象和这个函数内部的[[scope]]属性。在一个函数上下文中,变量对象被表示为活动对象(activation object)。当函数被调用者激活,这个特殊的活动对象(activation object) 就被建立了。它包含普通参数(formal parameters) 与特殊参数(arguments)对象(具备索引属性的参数映射表)以及this。活动对象在函数上下文中做为变量对象使用,被推入上下文最前端,执行完后被销毁。[[scope]]是全部父变量对象的层级链,处于当前函数上下文之上,在函数建立时存于其中。htm

var a = 1;
function fun(){
    var b = 2;
    
    function fn(){
        var c = 6;
        console.log(a+b+c);
    }
    fn();
}
fun();    //9

咱们已经知道ecmascript会给函数提供[[scope]]属性 ,fn函数在建立时得到[[scope]]属性,经过该属性访问到全部父上下文的变量。“fn”上下文的做用域链为:fnContext.Scope = [ fnContext.Ao + funContext.Ao + globalContext.VO ]。

在ECMAScript中,闭包与函数的[[scope]]直接相关。实际上,闭包是函数代码和其[[scope]]的结合。[[scope]]在函数建立时被存储,当函数进一步激活时,在变量对象的这个词法链(静态的存储于建立时)中,来自较高做用域的变量将被搜寻。

var a = 1;
function fun(){
    console.log(a);
}

function father(fn){
    var a = 2;
    (function(){
        fn();
    })();
}
father(fun);    //1;

 函数能够做为参数被传递给其余函数使用 ,这里的father函数并非fun函数的父做用域,fun函数父做用域globalContext中取得a的值为1。

在通常状况下,一个做用域链包括父级变量对象(variable object)(做用域链的顶部)、函数自身变量VO和活动对象(activation object)。在代码执行过程当中,若是使用with或者catch语句就会改变做用域链。不推荐使用with,在 ES5 严格模式中该标签已被禁止。推荐的替代方案是声明一个临时变量来承载你所须要的属性。

 ----------------

 参:http://www.cnblogs.com/TomXu/archive/2012/01/18/2312463.html

相关文章
相关标签/搜索