2
首发地址:https://github.com/jeuino/Blo...

前言

在上一篇《JavaScript 之内存空间》中,简单介绍了下 JavaScript 中的变量是如何存储的。本篇文章将总结一下变量和函数在运行时是如何查找并引用的。

变量对象

Image  11

《JavaScript 之执行上下文》中,我们知道了一段代码在开始执行时,首先会创建一个执行上下文。而当进入执行上下文时,就会创建一个变量对象,此时代码还未执行。

变量对象是与执行上下文相关的数据作用域,执行上下文中定义的所有变量和函数都保存在这个对象中。可以将变量对象理解为作用域这个抽象概念的实体,当代码执行时,是从变量对象中查找是否存在相应的变量的。它具体是如何查找的呢?我们继续往下看。

变量对象的创建,依次经历了以下三个过程:

  • 扫描当前执行上下文中的所有形参(针对函数执行上下文):该过程生成 Arguments 对象,并创建以形参变量名作为属性名,形参变量值作为属性值的变量对象属性;我们都知道,在函数中,有一个 arguments 对象,存储了所有传递给函数的参数,那么这个对象是哪里来的呢,其实它就是在这个阶段生成的。
  • 扫描当前执行上下文中的所有函数声明:该过程创建以函数名作为属性名,函数的引用地址作为属性值的变量对象属性;
  • 扫描当前执行上下文中的所有变量声明:该过程创建以变量名作为属性名,undefined 作为属性值的变量对象属性;(变量的赋值是在代码执行阶段进行的)

变量对象的创建,是优先扫描函数声明的,如果变量与函数同名,则以函数为主。下面简单解释下原因:
假设变量对象(作用域)中已经存在一个名为 foo 的属性,它代表一个函数引用。当扫描变量声明时,又遇到一个命名为 foo 的变量,解释器会询问变量对象(作用域)是否已经存在一个该名称的标识符,如果存在,解释器会忽略该指令,继续执行;否则它会要求变量对象声明一个命名为 foo 的属性,并赋值为 undefined。
Image  12

由于全局执行上下文和函数执行上下文中的变量对象有一些差异,所以下面对它们分别进行介绍。

全局执行上下文

在全局执行上下文中,变量对象初始化是全局对象,也就是 window 对象。因此所有声明的全局变量和函数都是作为 window 对象的属性和方法创建的。

什么是全局对象

全局对象是预定义的对象,作为 JavaScript 的全局函数和全局属性的占位符。通过使用全局对象,可以访问其他所有预定义的对象、函数和属性。

全局对象不是任何对象的属性,所以它没有名称。

在顶层 JavaScript 代码中,可以用关键字 this 引用全局对象。

因为全局对象是作用域链的头,这意味着在顶层 JavaScript 代码中声明的所有变量都将成为全局对象的属性。

函数执行上下文

在函数执行上下文中,变量对象就是其活动对象(activation object, AO)

什么是活动对象?

变量对象和活动对象其实是一个对象,两者意思相同,只是处于执行上下文的不同生命周期。

执行上下文的生命周期包括两个阶段:创建阶段和执行阶段。后续会写一篇文章单独总结。

变量对象就是在创建阶段时初始化的,此时代码还未执行,变量对象中的属性不能被访问。进入执行阶段后,开始逐行执行代码,此时变量对象就会被激活变成活动对象(AO),其各种属性才能被访问。此时就可以通过查找变量对象上是否存在某属性的方式查找声明的变量和函数,获取到引用后进行变量赋值、函数调用等操作。


我们一起来看下下面这段代码生成的变量对象是什么样子的:

function fn(a, b) {
    var c = 2;
    function fn1() {}
    var d = function () {};
}

fn(1, 2, 3);
// fn 执行上下文中的变量对象
VO = {
    Arguments: {
        0: 1,
        1: 2,
        2: 3,
        length: 3
    },
    a: 1,
    b: 2,
    c: undefined,
    fn1: <fn1 reference>,
    d: undefined
}

Image  13

此时的变量对象是在代码执行前创建的,变量对象中的属性都不能访问。在进入执行阶段之后,变量对象变成了活动对象,此时变量对象的属性可以被访问了。然后开始执行代码,代码执行的过程中可能会修改变量对象的属性值。

还是上面的例子,当代码执行完毕,这时的 AO 变成:

AO = {
    Arguments: {
        0: 1,
        1: 2,
        2: 3,
        length: 3
    },
    a: 1,
    b: 2,
    c: 2,
    fn1: <fn1 reference>,
    d: <FunctionExpression "d" reference>
}

总结:

  • 执行上下文中声明的所有变量和函数都保存在变量对象中;
  • 变量对象可以理解为作用域这个抽象概念的实体;
  • 变量对象在代码执行前就已经初始化好了,这很好的解释了变量和函数的提升问题;
  • 进入代码执行阶段,访问声明的变量和函数时,就会从当前执行上下文的变量对象中查找。

下一篇

在每个执行上下文中,都包括三个重要的属性:

  • 变量对象(Variable Object,VO)
  • 作用域链(Scope Chain)
  • this指向

下篇文章将开始介绍执行上下文中的this关键字。

传送门:《JavaScript 之this关键字》

参考:

JavaScript深入之变量对象
JavaScript 之深入理解执行上下文

Jojo
126 声望12 粉丝

Stick a little bit more every day