javascript 作用域链

页面导航:首页 > 网络编程 > JavaScript > javascript 作用域链

javascript 作用域链

来源: 作者: 时间:2016-01-19 18:32 【

执行环境(Execution Context) 所有的javascript代码都是在一个执行环境中被执行的。它只是一种机制,用来完成运行时作用域、生存期等方面的处理。 代码分为三种类型: Global Code Eval Code
执行环境(Execution Context)
 
  所有的javascript代码都是在一个执行环境中被执行的。它只是一种机制,用来完成运行时作用域、生存期等方面的处理。
 
  代码分为三种类型:
 
  Global Code 
  Eval Code
  Function Code 
  这是一个EC结构
 
 
 
可以理解如下:
 
 
activeExecutionContext = {
    VO: {...}, // or AO
    this: thisValue,
    Scope: [ // Scope chain
      // 所有变量对象的列表
      // for identifiers lookup
    ]
};

 

 
  当一段程序开始时,会先进入到全局执行上下文环境。此时如果调用了某些函数,就会进入他们的上下文环境,执行完之后再退出该上下文环境。
 
假设激活了一个EC1的上下文,图解如下
 
 
 
  变量对象(VO)和活动对象(AO)
 
  变量对象是一个与上下文相关的数据作用域,用于存储被定义在上下文中的变量和函数声明(不包括函数表达式)。在global全局上下文中,变量对象即使全局对象自身。
 
copy一个例子
 
 
var foo = 10;

function bar() {} // // 函数声明
(function baz() {}); // 函数表达式

console.log(
  this.foo == foo, // true
  window.bar == bar // true
);

console.log(baz); // 引用错误,baz没有被定义

 

全局上下文中的变量对象(VO)会有如下属性:
 
 
 
在一个函数的上下文中,变量对象被表示为活动对象
 
活动对象在进入上下文中初始化成了
 
AO = {
  arguments: <ArgO>
};
arguments属性值就是Arguments对象
 
之后就是存储当前上下文的变量与函数声明
 
  作用域链
 
  作用域链是一个 对象列表,用于检索上下文中出现的 标识符(变量名称、函数声明,普通参数)。
 
  在函数创建的时候,当前的作用域链被存储到了函数的[[scope]]属性中。当进入上下文创建AO/VO之后,上下文(EC)的Scope属性作了如下处理:
 
Scope = AO|VO + [[Scope]]
 
 
  知识点就是这些,现在我们来一段代码,把过程给串联起来。在这之前,我们要知道另一个知识,就是执行上下文的代码被分成两个基本的阶段来处理
 
进入执行上下文
执行代码
 
var x = 10;

function foo(m) {
  var y = 20;

  function bar() {
    var z = 30;
    alert(x +  y + z + m);
  }

  bar();
}

foo(10); // 60

 

首先先进入全局上下文环境
 
  全局上下文的变量对象(代码执行时,x才被赋值)是:
 
globalContext.VO === Global = {
  x: 10
  foo: <reference to function>
};
globalContext.Scope = globalContext.VO;
 
当"foo"创建时,
 
 
foo.[[Scope]] = [
  globalContext.Scope
];
//也就是
foo.[[Scope]] = [
  globalContext.VO
];

 

在"foo"激活时,进入了foo的上下文,foo上下文中的活动对象(代码执行时,y才被赋值)是:
 
fooContext.AO = {
 arguments:<Arg>,
 m: 10,
 y: 20,
  bar: <reference to function>
};
此时foo上下文中的作用域链为:
 
fooContext.Scope = fooContext.AO + foo.[[Scope]] // i.e.:
 
fooContext.Scope = [
  fooContext.AO,
  globalContext.VO
];
函数"bar"创建时,其[[scope]]为:
 
bar.[[Scope]] = fooContext.Scope;
//也就是
bar.[[Scope]] = [
  fooContext.AO,
  globalContext.VO
];
同理,bar就不写了。最后bar上下文的作用域链为:
 
 
barContext.Scope = barContext.AO + bar.[[Scope]] 

barContext.Scope = [
  barContext.AO,
  fooContext.AO,
  globalContext.VO
];

 

在alert执行时,作用域链中查找标示符如下
 
查找 x,  barContext.AO 无 ----> fooContext.AO 无 ---->globalContext.VO 有
 
查找 Z,  barContext.AO 有
 
其他的就自己看了。
 
  局部变量、全局变量
 
  在作用域链中查找标识符是需要花时间的,所以就明白为什么需要尽量使用局部变量(将频繁使用的全局变量缓存下来),全局变量尽量少用了。并且with会破坏作用域链,它会将指定的VO/AO加入到作用域链的顶端,这样在标识符查找时,需要查找更长的作用域链。
 
  闭包
 
  理解了上面的概念,闭包就很容易理解了。闭包是代码块和创建该代码块的上下文中数据的结合。
 
 
Tags:

文章评论

最 近 更 新
热 点 排 行
Js与CSS工具
代码转换工具

<