关于javascript的作用域的一些总结,主要参考以上文章,加上自己的整理的理解。
近日对javascript的作用域,以及它的运行过程,感觉不甚了解,尤其在使用闭包的时候,感觉都是模糊不清的。
与是在网上一探究竟,可是没想,网上的大多文章都是含糊其次,且大多雷同,经多方查找,加上自己看一些书。
最终对javascript有一丝丝了解。记录下来,以防忘记。
要彻底了解javascript的作用域就要了解一些概念。
1 运行期上下文在javascript中,只有函数能够创建出独立的作用域,需要注意的是for循环是不能创建的,否则你的代码就可能得到意想不到的结果。
for (var k in {a: 1, b: 2}) { alert(k);}alert(k);
即使循环结束,一样可以alert出k的值.
2变量对象以及活动对象(VO/AO)变量对象(缩写为VO)是一个与执行上下文相关的特殊对象,它存储着在上下文中声明的以下内容:
变量 (var,变量声明);
函数声明 (FunctionDeclaration,缩写为FD);
函数的形参
只有全局上下文的变量对象允许通过VO的属性名称来间接访问(因为在全局上下文里,全局对象自身就是变量对象),在其它上下文中是不能直接访问VO对象的,因为它只是内部机制的一个实现,通常使用AO(activation object来保存变量)。
VO保存变量对象示例:
var a = 10; functiontest(x) { var b = 20; }; test(30);
对应的变量对象是:
// 全局上下文的变量对象
VO(globalContext) = { a: 10, test:};// test函数上下文的变量对象VO(test functionContext) = { x: 30, b: 20};
首先,我们要给全局对象一个明确的定义:
全局对象(Global object) 是在进入任何执行上下文之前就已经创建了的对象;
这个对象只存在一份,它的属性在程序中任何地方都可以访问,全局对象的生命周期终止于程序退出那一刻。
全局对象初始创建阶段将Math、String、Date、parseInt作为自身属性,等属性初始化,同样也可以有额外创建的其它对象作为属性(其可以指向到全局对象自身)。例如,在DOM中,全局对象的window属性就可以引用全局对象自身(当然,并不是所有的具体实现都是这样):
global = { Math: <...>, String:<...> ... ... window: global//引用自身};
当访问全局对象的属性时通常会忽略掉前缀,这是因为全局对象是不能通过名称直接访问的。不过我们依然可以通过全局上下文的this来访问全局对象,同样也可以递归引用自身。例如,DOM中的window。综上所述,代码可以简写为:
String(10); // 就是global.String(10); // 带有前缀window.a = 10; // === global.window.a = 10 ===global.a = 10; this.b = 20; //global.b = 20;alert(this === window);这里可以看到,用winodow调用其实是用的VO中的一个属性,而用this,则是用的全局变量。因此,回到全局上下文中的变量对象——在这里,变量对象就是全局对象自己:VO(globalContext) === global;非常有必要要理解上述结论,基于这个原理,在全局上下文中声明的对应,我们才可以间接通过全局对象的属性来访问它(例如,事先不知道变量名称)。var a = new String('test');alert(a); // 直接访问,在VO(globalContext)里找到:testalert(window['a']); // 间接通过global访问:global === VO(globalContext): testalert(a === this.a); // truevar aKey = 'a';alert(window[aKey]); // 间接通过动态属性名称访问:test
在函数执行上下文中,VO是不能直接访问的,此时由活动对象(activation object,缩写为AO)扮演VO的角色。
VO(functionContext)=== AO;
活动对象是在进入函数上下文时刻被创建的,它通过函数的arguments属性初始化。arguments属性的值是Arguments对象:
AO = { arguments: Script相关的书籍都声称:“不管是使用var关键字(在全局上下文)还是不使用var关键字(在任何地方),都可以声明一个变量”。请记住,这是错误的概念:任何时候,变量只能通过使用var关键字才能声明。
上面的赋值语句:
a= 10;
这仅仅是给全局对象创建了一个新属性(但它不是变量)。“不是变量”并不是说它不能被改变,而是指它不符合ECMAScript规范中的变量概念,所以它“不是变量”(它之所以能成为全局对象的属性,完全是因为VO(globalContext) === global,大家还记得这个吧?)。
让我们通过下面的实例看看具体的区别吧:
alert(a); // undefinedalert(b); // b 没有声明b = 10;var a = 20;
所有根源仍然是VO和进入上下文阶段和代码执行阶段:
进入上下文阶段:
VO = {a: undefined};
我们可以看到,因为“b”不是一个变量,所以在这个阶段根本就没有“b”,“b”将只在代码执行阶段才会出现(但是在我们这个例子里,还没有到那就已经出错了)。
让我们改变一下例子代码:
alert(a); // undefined, 这个大家都知道,b = 10;alert(b); // 10, 代码执行阶段创建var a = 20;
关于变量,还有一个重要的知识点。变量相对于简单属性来说,变量有一个特性(attribute):{DontDelete},这个特性的含义就是不能用delete操作符直接删除变量属性。
a = 10;alert(window.a); // 10alert(delete a); // truealert(window.a); // undefinedvar b = 20; alert(window.b); // 20alert(delete b); // falsealert(window.b); // still 20但是这个规则在有个上下文里不起走样,那就是eval上下文,变量没有{DontDelete}特性。eval('var a = 10;');alert(window.a); // 10alert(delete a); // truealert(window.a); // undefined
使用一些调试工具(例如:Firebug)的控制台测试该实例时,请注意,Firebug同样是使用eval来执行控制台里你的代码。因此,变量属性同样没有{DontDelete}特性,可以被删除。
如果能认真看完博客,相信你一定对javascript很感兴趣。
文档如果对你有一丝丝的帮助,那么恭喜。