最近一直想整理几篇好的文章分享给大家,无奈文笔太俗,也是一直懒惰。直到今天才稍微有个样子写出来,因为明天就不上班了。
本文都是自己平时经验积累,难免错误,欢迎指正,请勿拍砖。
开始。
在javascript中,如果说原型链是对象寻找属性的过程,那么作用域链就是在作用域内寻找变量的过程,那么属性和变量又有什么不同吗?
下面举个例子:
function test(){ var a=1; } var obj={b:2}
上面函数test中的a就是变量,使用var 关键字定义,上面的obj对象中的b就是属性,可以知道属性b属于obj这个对象,那么变量a属于哪个对象吗?答案是肯定的,变量a 也是属于某个对象的,接下来我们会介绍这个对象。
介绍这个对象之前,我们必须要了解一下函数的的执行过程,每个函数对应着一个执行上下文,函数执行的过程有两个阶段,一是进入执行上下文,二是执行代码。
进入执行上下文,每个函数都会对应着一个上下文对象(我们姑且这么叫),这个对象有个VO 属性(我们也姑且这么叫),在本函数内定义的变量都是这个VO对象的属性,当然上下文对象和VO对象还有其他的属性内容。
下面通过实例了解一下上下文对象以及VO对象。
function test(i){ alert(x); // function var x = 10; alert(x); // 10 function x() {}; alert(x); // 10 } test(10);
很多同学对上面的代码运行结果可能有些疑惑,我们来分析一下上面代码中test函数对应的上下文对象以及VO对象。
第一阶段,进入执行上下文
testExecutionContext = { VO: { arguments: { 0:10 length: 1 }, x: pointer to function x(), }, scopeChain: { ... }, this: { ... } }
第二阶段,执行代码
testExecutionContext = { VO: { arguments: { 0:10 length: 1 }, x: 10, }, scopeChain: { ... }, this: { ... } }
依据这个分析,我们不难得出上面的函数运行结果了。
下面说点题外话
var a=10; b=12;上面这个代码有何不同呢?有人可能会说,这是定义了两个全局的变量,其实不然,前者是一个全局变量,后者只是为window对象定义了一个属性。但它们到底何不同呢?这里通过执行上下文就能清晰分析出它们的不同之处
alert(a);//undefined var a=10; alert(b);//脚本错误 b=10;看见了吧,这就是区别,用上下文的方式分析一下。
进入执行上下文,b根本就不在VO对象当中。
VO = {
a: undefined
};
执行代码阶段,才有这个两个值。
VO = {
a: 10
b:10
};
下面回到正题,说作用域链,既然是链,那就不能是一个东西,必须是一串东西。
上例子:
function a(){ var aa="aa"; function b(){ var bb="bb"; function c(){ var cc="cc"; alert(aa); alert(bb); alert(cc); } c() } b(); } a();我们上面的代码,我们在函数c 里面可以使用函数b,函数a里面的变量 bb和aa。其实这就一个链,一层一层的链状结构,内部函数可以使用外部函数的变量。这很简单,下面稍微深入讨论一下原理。
每个函数都有个内部的属性__parent__,这个属性是我们用浏览器访问不到的,如果要访问可以使用Rhino解释器来执行下面的代码。关于这个东西,网上自行搜索吧。
__parent__属性可以获取“上级函数”的VO对象,有些拗口,用上面的例子说吧,函数c,c.__parent__ 就是函数b对应的上下文的VO对象
function a(){ var aa="aa"; function b(){ var bb="bb"; function c(){ var cc="cc"; for(var i in c.__parent__){//这里是函数b的VO对象 println(i); //分别是arguments,bb,c } } c() } b(); } a();
上面println(Rhino没有alert)的结果就是函数b对应的VO对象,里面包含参数对象,bb变量,和函数c。
同理,c.__parent__.__parent__ 对应的就是函数a的VO对象 改写上面的代码: for(var i in c.__parent__.__parent__){//这里就是函数a的VO对象 println(i); //分别是arguments,aa,b } 到这里我们就得出原理,也对作用域链以及作用域寻找变量的过程应该比较清晰了。
下面对上面的a,b,c函数的作用域画个图简单了解一下。
通过上面的图解,我们就可以清晰的发现这确实是一个链,例如:c函数寻找一个变量,会现在c函数的VO对象里面寻找,找不到的话,去c.__parent__也就是b函数的VO对象上寻找,再找不到的话会去c.__parent__.__parent__上也就是a函数的VO对象去找,再找不到的话就回去c.__parent__.parent__.parent__去找也就是全局作用域了,如果还是找不到,没有办法了,直接出错了。是不是很原型链有的一拼。
哈哈,结束。
时间匆匆,难免错误,编辑器也永不好,还望见谅,祝大家新年快乐。
原文:http://my.oschina.net/mam/blog/377889