重新JavaScript(5)- 作用域链

74 阅读2分钟

作用域和作用域链是两个概念。

作用域:表示当前执行代码对变量的访问权限,具体参考系列的第二部分。

作用域链:是由当前执行上下文与上层环境的一系列变量对象组成,它保证了当前执行环境对符合访问权限的变量和函数的有序访问。

作用域是给的一个定义规则,而作用域链是JavaScript语言实际执行的情况。作用域链是在声明阶段确定的

下面我们举个例子,同时回顾前面的几篇的内容

var a = 20;

function test() {
  var b = a + 10;

  function innerTest() {
    var c = 10;
    return b + c;
  }

  return innerTest();
}

test();
  1. 上面代码执行时,首先初始化全局对象VO和作用域链scopeChain,此时scopeChain只有全局的VO, 然后向ECS中push一个全局上下文globalcontent,
// 全局上下文
globalcontent = {
	VO:{
		a: 20
	},
    scopeChain:[VO(global)]
}

ESC   = [globalcontent]
  1. 执行代码,当执行到test函数时,创建执行上下文testEC,包括初始化变量对象VO,同时向作用域链scopeChain中添加当前的VO,并将其push到栈中。
// test执行上下文
testEC = {
	VO = {
        arguments: {
            0: undefined,
            length: 0
        }
        b: undefined,
        innerTest: <innerTest reference>,  // 表示foo的地址引用
    },
    scopeChain:[VO(test),VO(global)]
}


ESC = [globalcontent, testEC]
  1. 当执行test函数时,testEC中的VO转变为AO,并重新赋值变量,如下
// test执行上下文
testEC = {
	AO = {
        arguments: {
            0: undefined,
            length: 0
        }
        b: 30,
        innerTest: <innerTest reference>,  // 表示foo的地址引用
    },
    scopeChain:[AO(test),VO(global)]
}


ESC = [globalcontent, testEC]
  1. 当执行到return语句时,创建执行上下文innerTestEC,包括初始化变量对象VO,同时向作用域链scopeChain中添加当前的VO,并将其push到栈中。
// innerTest执行上下文
innerTestEC = {
	VO = {
        arguments: {
            0: undefined,
            length: 0
        }
        c: undefined,
    },
    scopeChain:[VO(innerTest),AO(test),VO(global)]
}


ESC = [globalcontent, testEC,innerTestEC]

5、执行innerTest函数时,innerTestEC的VO转变为AO,并重新赋值变量,如下

// innerTest执行上下文
innerTestEC = {
	AO = {
        arguments: {
            0: undefined,
            length: 0
        }
        c: 10,
    },
    scopeChain:[AO(innerTest),AO(test),VO(global)]
}


ESC = [globalcontent, testEC,innerTestEC]

总结:

作用域链是由一系列变量对象组成的链条,其中链条的最前

端是当前执行环境的AO,最末端为全局对象。如果要查询变量对象的标识符,就可以通过这个链条,从前往后,逐层访问作用域中的变量。