JavaScript作用域链
来源:互联网 发布:小米手机 网络连接失败 编辑:程序博客网 时间:2024/05/17 10:05
上一篇文章中介绍了Execution Context中的三个重要部分:VO/AO,scope chain和this,并详细的介绍了VO/AO在JavaScript代码执行中的表现。
本文就看看Execution Context中的scope chain。
作用域
开始介绍作用域链之前,先看看JavaScript中的作用域(scope)。在很多语言中(C++,C#,Java),作用域都是通过代码块(由{}包起来的代码)来决定的,但是,在JavaScript作用域是跟函数相关的,也可以说成是function-based。
例如,当for循环这个代码块结束后,依然可以访问变量”i”。
for(var i = 0; i < 3; i++){ console.log(i);}console.log(i); //3
对于作用域,又可以分为全局作用域(Global scope)和局部作用域(Local scpoe)。
全局作用域中的对象可以在代码的任何地方访问,一般来说,下面情况的对象会在全局作用域中:
最外层函数和在最外层函数外面定义的变量
没有通过关键字”var”声明的变量
浏览器中,window对象的属性
局部作用域又被称为函数作用域(Function scope),所有的变量和函数只能在作用域内部使用。
var foo = 1;window.bar = 2;function baz(){ a = 3; var b = 4;}// Global scope: foo, bar, baz, a // Local scope: b
作用域链
通过前面一篇文章了解到,每一个Execution Context中都有一个VO,用来存放变量,函数和参数等信息。
在JavaScript代码运行中,所有用到的变量都需要去当前AO/VO中查找,当找不到的时候,就会继续查找上层Execution Context中的AO/VO。这样一级级向上查找的过程,就是所有Execution Context中的AO/VO组成了一个作用域链。
所以说,作用域链与一个执行上下文相关,是内部上下文所有变量对象(包括父变量对象)的列表,用于变量查询。
Scope = VO/AO + All Parent VO/AOs
var x = 10;function foo() { var y = 20; function bar() { var z = 30; console.log(x + y + z); }; bar()};foo();
上面代码的输出结果为”60”,函数bar可以直接访问”z”,然后通过作用域链访问上层的”x”和”y”。
绿色箭头指向VO/AO
蓝色箭头指向scope chain(VO/AO + All Parent VO/AOs)
再看一个比较典型的例子:
var data = [];for(var i = 0 ; i < 3; i++){ data[i]=function() { console.log(i); }}data[0]();// 3data[1]();// 3data[2]();// 3
第一感觉(错觉)这段代码会输出”0,1,2”。但是根据前面的介绍,变量”i”是存放在”Global VO”中的变量,循环结束后”i”的值就被设置为3,所以代码最后的三次函数调用访问的是相同的”Global VO”中已经被更新的”i”。
结合作用域链看闭包
在JavaScript中,闭包跟作用域链有紧密的关系。相信大家对下面的闭包例子一定非常熟悉,代码中通过闭包实现了一个简单的计数器。
function counter() { var x = 0; return { increase: function increase() { return ++x; }, decrease: function decrease() { return --x; } };}var ctor = counter();console.log(ctor.increase());console.log(ctor.decrease());
下面我们就通过Execution Context和scope chain来看看在上面闭包代码执行中到底做了哪些事情。
当代码进入Global Context后,会创建Global VO
绿色箭头指向VO/AO
蓝色箭头指向scope chain(VO/AO + All Parent VO/AOs)当代码执行到”var cter = counter();”语句的时候,进入counter Execution Context;根据上一篇文章的介绍,这里会创建counter AO,并设置counter Execution Context的scope chain
- 当counter函数执行的最后,并退出的时候,Global VO中的ctor就会被设置;这里需要注意的是,虽然counter Execution Context退出了执行上下文栈,但是因为ctor中的成员仍然引用counter AO(因为counter AO是increase和decrease函数的parent scope),所以counter AO依然在Scope中。
- 当执行”ctor.increase()”代码的时候,代码将进入ctor.increase Execution Context,并为该执行上下文创建VO/AO,scope chain和设置this;这时,ctor.increase AO将指向counter AO。
绿色箭头指向VO/AO
蓝色箭头指向scope chain(VO/AO + All Parent VO/AOs)
红色箭头指向this
黑色箭头指向parent VO/AO
相信看到这些,一定会对JavaScript闭包有了比较清晰的认识,也了解为什么counter Execution Context退出了执行上下文栈,但是counter AO没有销毁,可以继续访问。
二维作用域链查找
通过上面了解到,作用域链(scope chain)的主要作用就是用来进行变量查找。但是,在JavaScript中还有原型链(prototype chain)的概念。
由于作用域链和原型链的相互作用,这样就形成了一个二维的查找。
对于这个二维查找可以总结为:当代码需要查找一个属性(property)或者描述符(identifier)的时候,首先会通过作用域链(scope chain)来查找相关的对象;一旦对象被找到,就会根据对象的原型链(prototype chain)来查找属性(property)。
下面通过一个例子来看看这个二维查找:
var foo = {}function baz() { Object.prototype.a = 'Set foo.a from prototype'; return function inner() { console.log(foo.a); }}baz()(); // Set bar.a from prototype
对于这个例子,可以通过下图进行解释,代码首先通过作用域链(scope chain)查找”foo”,最终在Global context中找到;然后因为”foo”中没有找到属性”a”,将继续沿着原型链(prototype chain)查找属性”a”。
蓝色箭头表示作用域链查找
橘色箭头表示原型链查找
总结
本文介绍了JavaScript中的作用域以及作用域链,通过作用域链分析了闭包的执行过程,进一步认识了JavaScript的闭包。
同时,结合原型链,演示了JavaScript中的描述符和属性的查找。
转载自:
http://www.cnblogs.com/wilber2013/p/4909459.html
参考:
http://blog.csdn.net/taolong1/article/details/25009521
- javascript 作用域链
- Javascript作用域链
- javascript 作用域链
- Javascript作用域链
- JavaScript作用域链
- JavaScript作用域链
- javascript作用域链
- Javascript 作用域链
- JavaScript作用域链
- Javascript作用域链
- Javascript作用域链
- Javascript作用域链
- JavaScript作用域链
- JavaScript作用域链
- JavaScript作用域链
- JavaScript作用域链
- JavaScript作用域链
- JavaScript作用域链
- 数据压缩实验五:JPEG解码
- C++多态性(待补充)
- 分布式服务架构和面向服务架构
- Android API解析Paint&Canvas
- C/C++全局变量和局部变量作用域问题
- JavaScript作用域链
- solr报错PERFORMANCE WARNING: Overlapping onDeckSearchers=2
- STM32基础知识3-STM32串口USART1的使用方法和程序
- 转-【工具使用之】- IntelliJ IDEA 刚刚使用时,常用且必须知道和设置的配置
- Java8collect、reduce方法聚合操作详解
- clumsy 能在 Windows 平台下人工造成不稳定的网络状况,方便你调试应用程序在极端网络状况下的表现。
- 用webdriver+phantomjs实现无浏览器的自动化
- 编辑器设置知识整理
- 修改端口号