🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] # 变量对象(Variable Object) 变量对象中存储了在上下文(环境)中定义的变量和函数声明。 <br> 变量对象的创建,依次经历了以下几个过程。 1. 建立 arguments 对象。函数的所有形参 (如果是函数上下文) * 由名称和对应值组成的一个变量对象的属性被创建 * 没有实参,属性值设为 undefined 2. 检查当前上下文的函数声明,也就是使用 function 关键字声明的函数。 * 由名称和对应值(函数对象(function-object))组成一个变量对象的属性被创建 * 如果变量对象已经存在相同名称的属性,则完全替换这个属性 3. 检查当前上下文中的变量声明 * 由名称和对应值(undefined)组成一个变量对象的属性被创建; * 如果变量名称跟已经声明的形式参数或函数相同,则变量声明不会干扰已经存在的这类属性 ![](https://box.kancloud.cn/cd1f070399af3a1e1a79324b51630078_701x350.png) 在上面的规则中我们看出,function声明会比var声明优先级更高一点。 # 活动对象(Activation Object) 活动对象是在函数执行上下文里面的,其实也是变量对象,只是它需要在函数被调用时才被激活。 <br> # 例子 ## 例1 ~~~ function foo(a) { var b = 2; function c() {} var d = function() {}; b = 3; } foo(1); ~~~ 在进入执行上下文后,这时候的 AO 是: ~~~js AO = { arguments: { 0: 1, length: 1 }, a: 1, b: undefined, c: reference to function c(){}, d: undefined } ~~~ 代码执行 在代码执行阶段,会顺序执行代码,根据代码,修改变量对象的值 还是上面的例子,当代码执行完后,这时候的 AO 是: ~~~js AO = { arguments: { 0: 1, length: 1 }, a: 1, b: 3, c: reference to function c(){}, d: reference to FunctionExpression "d" } ~~~ <br> ## 例2 ~~~ function test() { console.log(foo); console.log(bar); var foo = 'Hello'; console.log(foo); var bar = function () { return 'world'; } function foo() { return 'hello'; } } test(); // 创建阶段 VO = { arguments: {...}, foo: <foo reference>, bar: undefined } // 这里有一个需要注意的地方,因为var声明的变量当遇到同名的属性时,会跳过而不会覆盖 // 执行阶段 VO -> AO VO = { arguments: {...}, foo: 'Hello', bar: <bar reference> } ~~~ <br> ## 例3 ~~~ console.log(a); console.log(b); console.log(c); console.log(d); var a = 100; b = 10; function c(){}; var d = function(){}; ~~~ **上述代码的变量对象:** ~~~ //这里用VO表示变量对象 VO = { a = undefined; //有a,a使用var声明,值会被赋值为undefined //没有b,因为b没用var声明 c = function c (){} //有c,c是函数声明,并且c指向该函数 d = undefined; //有d,d用var声明,值会被赋值为undefined } ~~~ 执行上述代码的时候,会创建一个全局执行上下文,上下文中包含上面变量对象,创建完执行上下文后,这个执行上下文才会被压进执行栈中。开始执行后,因为js代码一步一步被执行,后面赋值的代码还没被执行到,所以使用console.log函数打印各个变量的值是变量对象中的值。 在运行到第二行时会报错(报错后就不再执行了),因为没有b(b is no defined)。把第二行注释掉后,再执行各个结果就是VO里面的对应的值。 <br> # 全局上下文的变量对象 以浏览器中为例,全局对象为window。 全局上下文有一个特殊的地方,它的变量对象,就是window对象。而这个特殊,在this指向上也同样适用,this也是指向window。 ~~~ // 以浏览器中为例,全局对象为window // 全局上下文 windowEC = { VO: window, scopeChain: {}, this: window } ~~~ 除此之外,全局上下文的生命周期,与程序的生命周期一致,只要程序运行不结束,比如关掉浏览器窗口,全局上下文就会一直存在。其他所有的上下文环境,都能直接访问全局上下文的属性。 <br> # 总结 到这里变量对象的创建过程就介绍完了,让我们简洁的总结我们上述所说: 1. 全局上下文的变量对象初始化是全局对象 2. 函数上下文的变量对象初始化只包括 Arguments 对象 3. 在进入执行上下文时会给变量对象添加形参、函数声明、变量声明等初始的属性值 4. 在代码执行阶段,会再次修改变量对象的属性值 未进入执行阶段之前,变量对象中的属性都不能访问!但是进入执行阶段之后,变量对象转变为了活动对象,里面的属性都能被访问了,然后开始进行执行阶段的操作。 <br> # 参考资料 * [JS变量对象详解](https://www.cnblogs.com/lsgxeva/p/7976034.html) * [JavaScript深入之变量对象](https://github.com/mqyqingfeng/Blog/issues/5)