作用域链与[[scope]]
通过调用 Function 构造函数创建的函数对象,其内部的 [[scope]] 属性引用的作用域链中始终只包含全局对象。
- var aFunction = new Function( a, b, "return a+b" );
aFunction.[[scope]] -> globalObject (在javascript 为 window对象)
通过函数声明①或函数表达式②创建的函数对象,其内部的 [[scope]] 属性引用的则是创建它们的执行环境的作用域链。
①
- function someFunction ( Parameters ) {
- do someting...
- }
②
- var someFunction = function( Parameters ) {
- do something...
- }
①创建过程如下
1. 在全局的执行环境中,进行变量实例化,根据声明创造相应函数对象(全局执行环境的[[scope]]只包含全局对象)
2. 将函数的[[scope]]指向只包含全局变量的作用链
参考下图
下面谈谈自己对1、2两步的理解:
1为全局对象添加了属性,通过这个属性可以在全局执行环境中调用someFunction,例如:
- function someFunction(a, b) {
- alert( a + b );
- }
- window.someFunction( 2, 3);
window即全局变量通过someFunction这个属性调用了函数
2使得函数可以通过[[scope]],因为它指向全局对象,调用全局对象中的其他属性和变量,例如:
- window.var1 = "HuJin";
- function anontherFunction() {
- alert(var1);
- }
- anontherFunction();
PS:有必要解释一下2中的“指向”,“指向”的意思是加入其中,也就是将全局变量的对象加入到输入本来的[[scope]]中。举例说明:
- window.var1 = "HuJin";
- function anontherFunction() {
- var1 = "CaoLixiang";
- alert(var1);
- }
- anontherFunction();
所以说“指向”并不是说anotherFunction的作用域发生了替换,而是全局变量被加入到了原来的[[scope]]中,即:
annotherFunction.[[scope]](其中包含 var1 = "CaoLixiang") = annotherFunction的活动对象(其中包含 var1 = "CaoLixiang") + 全局对象(其中包含var1 = "HuJin")
上面弹出“HuJin”的例子中,函数先会寻找活动对象的var1属性——annotherFunction活动对象,寻找未果,然后上诉至全局对象。所以这里反映了闭包的强大功能,可以访问外函数的属性和变量。
②创建过程如下
1.在全局执行环境的变量实例化过程中,会先为全局对象创建一个命名属性。而在计算赋值语句之前,暂时不会创建函数对象,也不会将该函数对象的引用指定给全局对象的命名属性。
2.但是,当计算函数表达式时最终还是会在全局执行环境中创建这个函数对象,而为这个创建的函数对象的 [[scope]] 属性指定的作用域链中仍然只包含全局对象。
3.内部的函数声明或表达式会导致在包含它们的外部函数的执行环境中创建相应的函数对象,因此这些函数对象的作用域链会稍微复杂一些。
最后一部分摘录浩行天下的博客——www.ghsky.com——深入理解javascript闭包(2)的部分内容,作为完结。
函数声明是不会受到with
语句的影响,因为它们的函数对象实在变量实例化阶段创建的,因此此时没有with
语句的影响。但是对于函数表达式就不同了,它们可能在一个with
语句块中执行:
- /* create a global variable - y - that refers to an object:- */
- var y = { x: 5 }; // object literal with an - x - property
- function exampleFuncWith() {
- var z;
- /* Add the object referred to by the global variable - y - to the
- front of he scope chain:-
- */
- with(y){
- /* evaluate a function expression to create a function object
- and assign a reference to that function object to the local
- variable - z - :-
- */
- z = function() {
- // inner function expression body;
- }
- }
- }
- /* execute the - exampleFuncWith - function:- */
- exampleFuncWith();
当exampleFuncWith
函数被调用的时候自然会产生一个新的执行上下文,其中的作用域链包含Activation对象之后紧接着全局对象。当执行到with
语句的时候,会将全局变量y
添加到作用域最前,这时候恰好碰到一个函数表达式的执行。函数表达式执行产生的函数对象其中的[[scope]]
属性被赋值为当前创建其的执行上下文,那么显而易见,由于刚刚所说的,现在的执行上下文中包含y
,且其位置还要在Activation
对象之前,此时作用域链的排列是这样的:y –> Activation –> global object
,因此这个函数表达式就受到with
语句的影响。
当与 with 语句相关的语句块执行结束时,执行环境的作用域得以恢复(y 会被移除),但是已经创建的函数对象z的 [[scope]] 属性所引用的作用域链中位于最前面的仍然是对象 y。
最后依然是用一张图做总结,下图是对例子形象描述:
从图中很容易发现,如果在不存在with
语句时,函数对象的[[scope]]
属性与外层函数(或者全局对象)的执行上下文有着密切的联系,这种联系也就形成了作用域链,建立了内外函数的沟通桥梁,这样也便很容易理解为什么内层函数可以访问外层函数局部变量。