Javascript做用域链

对于做用域,能够分为全局做用域和局部做用域javascript

全局做用域css

全局做用域中的对象能够在代码中的任何地方访问,通常来讲,下面状况的对象会在全局做用域中:html

  • 最外层函数和在最外层函数外面定义的变量
  • 没有经过关键字"var"声明的变量
  • 浏览器中,window对象的属性

局部做用域前端

局部做用域又称函数做用域,全部的变量和函数只能在做用域内部使用。html5

 

var foo = 1; window.bar = 2; function baz(){ a = 3; var b = 4; } // Global scope: foo, bar, baz, a  // Local scope: b

 

做用域链java

在Javascript代码运行中,全部用到的变量都须要去当前变量对象中查找,当找不到的时候,就会继续查找上层执行环境中的变量对象,这样一级级向上查找的过程,就是全部执行环境中的变量对象组成了一个做用域链。python

因此说,做用域链与一个执行上下文相关,是内部上下文全部变量对象(包括父变量对象)的列表,用于变量查询。android

看一个例子:ios

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"。git

绿色箭头指向变量对象/活动对象

蓝色箭头指向做用域链

 再看一个比较典型的例子:

var data = []; for(var i = 0 ; i < 3; i++){ data[i]=function() { console.log(i); } } data[0]();// 3
data[1]();// 3
data[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 cter = counter(); console.log(ctor.increase()); console.log(ctor.decrease());

下面咱们就经过Execution Context和scope chain来看看在上面闭包代码执行中到底作了哪些事情。

1. 当代码进入Global Context后,会建立全局变量对象

2. 当代码执行到”var cter = counter();”语句的时候,进入counter Execution Context;根据上一篇文章的介绍,这里会建立counter AO,并设置counter Execution Context的scope chain

3. 当counter函数执行的最后,并退出的时候,Global VO中的ctor就会被设置;这里须要注意的是,虽然counter Execution Context退出了执行上下文栈,可是由于ctor中的成员仍然引用counter AO(由于counter AO是increase和decrease函数的parent scope),因此counter AO依然在Scope中。

 

4. 当执行”ctor.increase()”代码的时候,代码将进入ctor.increase Execution Context,并为该执行上下文建立VO/AO,scope chain和设置this;这时,ctor.increase AO将指向counter AO。

 

相信看到这些,必定会对JavaScript闭包有了比较清晰的认识,也了解为何counter Execution Context退出了执行上下文栈,可是counter AO没有销毁,能够继续访问。

 

 
 
 
 
 

理解JavaScript的做用域链

上一篇文章中介绍了Execution Context中的三个重要部分:VO/AO,scope chain和this,并详细的介绍了VO/AO在JavaScript代码执行中的表现。

本文就看看Execution Context中的scope chain。

做用域

开始介绍做用域链以前,先看看JavaScript中的做用域(scope)。在不少语言中(C++,C#,Java),做用域都是经过代码块(由{}包起来的代码)来决定的,可是,在JavaScript做用域是跟函数相关的,也能够说成是function-based。

例如,当for循环这个代码块结束后,依然能够访问变量”i”。

对于做用域,又能够分为全局做用域(Global scope)和局部做用域(Local scpoe)。

全局做用域中的对象能够在代码的任何地方访问,通常来讲,下面状况的对象会在全局做用域中:

  • 最外层函数和在最外层函数外面定义的变量
  • 没有经过关键字”var”声明的变量
  • 浏览器中,window对象的属性

局部做用域又被称为函数做用域(Function scope),全部的变量和函数只能在做用域内部使用。

 

做用域链

经过前面一篇文章了解到,每个Execution Context中都有一个VO,用来存放变量,函数和参数等信息。

在JavaScript代码运行中,全部用到的变量都须要去当前AO/VO中查找,当找不到的时候,就会继续查找上层Execution Context中的AO/VO。这样一级级向上查找的过程,就是全部Execution Context中的AO/VO组成了一个做用域链。

因此说,做用域链与一个执行上下文相关,是内部上下文全部变量对象(包括父变量对象)的列表,用于变量查询。

看一个例子:

上面代码的输出结果为”60″,函数bar能够直接访问”z”,而后经过做用域链访问上层的”x”和”y”。

  • 绿色箭头指向VO/AO
  • 蓝色箭头指向scope chain(VO/AO + All Parent VO/AOs)

再看一个比较典型的例子:

第一感受(错觉)这段代码会输出”0,1,2″。可是根据前面的介绍,变量”i”是存放在”Global VO”中的变量,循环结束后”i”的值就被设置为3,因此代码最后的三次函数调用访问的是相同的”Global VO”中已经被更新的”i”。

结合做用域链看闭包

在JavaScript中,闭包跟做用域链有紧密的关系。相信你们对下面的闭包例子必定很是熟悉,代码中经过闭包实现了一个简单的计数器。

下面咱们就经过Execution Context和scope chain来看看在上面闭包代码执行中到底作了哪些事情。

1. 当代码进入Global Context后,会建立Global VO

.

  • 绿色箭头指向VO/AO
  • 蓝色箭头指向scope chain(VO/AO + All Parent VO/AOs)

 

2. 当代码执行到”var cter = counter();”语句的时候,进入counter Execution Context;根据上一篇文章的介绍,这里会建立counter AO,并设置counter Execution Context的scope chain

3. 当counter函数执行的最后,并退出的时候,Global VO中的ctor就会被设置;这里须要注意的是,虽然counter Execution Context退出了执行上下文栈,可是由于ctor中的成员仍然引用counter AO(由于counter AO是increase和decrease函数的parent scope),因此counter AO依然在Scope中。

4. 当执行”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”。

 

相关文章
相关标签/搜索