# JS-变量对象

🐴

# 前言

我们在《JS-执行上下文》中已经讨论过执行上下文,并且提到在执行代码执行中,执行上下文会初始化三个很重要的对象(变量对象作用域链this),这里我们讲解其中的一个变量对象。

# 变量对象

变量对象(Variable object)对象作为一个对象,我们可以使用伪代码来表示:

// 伪代码
Vo = {}

变量对象(Variable object)作为执行上下文的一个对象属性,也会因为执行上下文的不同,而有所不同。下面我们来看看全局上下文中的变量对象函数上下文中的变量对象

# 全局上下文变量对象

在说全局上下文中的变量对象对象之前我们先来确定一下全局对象

全局对象

全局对象是预定义的对象,作为JavaScript的全局函数和全局属性的占位符。通过使用全局对象,可以访问所有其他所有预定义的对象(比如Math)、函数(比如parseInt)和属性(比如innerHeight)。全局对象不是任何对象的属性。

全局对象(Global object) 是在进入任何执行上下文之前就已经创建了的对象; 这个对象只存在一份,它的属性在程序中任何地方都可以访问,全局对象的生命周期终止于程序退出那一刻。

在全局对象中我们可以通过this来引用,并且在DOM中,全局对象有一个window属性也可以应用自身

console.log(this)
console.log(window)
console.log(this === window) // true

除了上面提到的属性,函数等等,全局对象还包含很多的属性、函数。在DOM中还包含大量的DOM的节点信息以及事件。

另外在全局中声明的变量或函数,也会自动添加到全局对象上

var a = 1;
function f(){

}
console.log(window.a)  // 1
console.log(window.f)  // 函数 f

在全局中访问全局变量或函数时,我们还可以省略window对象,直接访问console.log(a)

打了这么多字,其实就是想说:全局对象就是全局上下文中的变量对象

Vo(globalContext) = global

# 函数上下文中的变量对象

函数上下文中的变量对象和全局上下文中的变量对象有所不同。在函数中通常是用活动对象(activation object) 来表示变量对象(Variable object),其实他俩是一个东西VO(functionContext) === AO,只不过变量对象(VO)不能直接访问,并且活动对象(activation object) 只有在函数激活(执行)时会产生。其他情况下无法访问活动对象

在进入函数执行上下文时,变量对象会被激活成为活动对象(AO),在活动对象中为代码的执行做一些初始化,比如变量和函数的提升,初始化arguments属性,并为arguments赋值为Arguments对象。

// 伪代码
AO = {
    var:{}   // 初始化变量
    function:{} // 初始化函数声明
    arguments:Arguments // 初始化arguments 对象
}

# 可执行代码执行过程

JavaScript中执行上下文中的代码被分为分两个阶段来执行:

  • 创建阶段(进入执行上下文阶段)
  • 执行阶段

下面我们来看看这两个阶段的执行。

# 创建阶段

进入执行上下文阶段时,为代码的执行做前期准备,不只是准备变量对象,还好建立作用域链、初始化this,这里我们只讲变量对象的处理。在以后的文章中我们会讨论作用域链this

  • 生成变量对象 (创建arguments, 检索函数声明,检索变量声明)
  • 建立作用域链
  • 确定this的指向

全局环境中,在进入执行上下文时,变量就是全局对象,拥有全局对象的所有属性方法。另外在变量对象中会将var声明的变量(ES6中的letconst不会被提升)存储起来,并赋值为undefined, 并且将函数声明提升(函数表达式作为变量处理)。例如下面这段代码

var a = 1;
function f(){}
var cf = function(){}
b = 2;

在上面代码在执行前,变量对象会是这样:

// 伪代码
VO(globalContext) = {
    Math,
    Window:global,
    ... // 等等全局属性、函数或对象
    a:undefined, // 变量a被提升,并赋值为undefined
    cf:undefined, // 函数表达式cf 按变量处理
    f:<f> // 函数声明f被提升,并指向函数f
}

这是代码在执行阶段前,执行上下文中变量对象所做的一些前期工作。函数执行上下文中的活动对象和全局执行上下文中的变量对象在代码执行前处理大同小异,只是活动对象中的一些属性不同。

function fun(){
    var b = true;
    function text(){}
}
fun()

在上面代码在执行前,活动对象会是这样:

// 伪代码
AO(funContext) = {
    arguments:Arguments, // arguments 对象初始化
    b:undefined, // 变量b被提升,并赋值为undefined
    text:<text> // 函数声明text被提升,并指向函数text
}

当变量明和函数名称相同时,变量对象会先提升变量,在提升函数声明,如果名称相同就覆盖变量

var f = 1;
function f(){}

// 伪代码
VO(globalContext) = {
    f:<f> // 函数声明f被提升,和变量f同名,覆盖变量值
}

# 执行阶段

执行代码的前期准备工作完成后,该进入代码的执行阶段了,在这个阶段,主要做了下面这下事:

  • 变量赋值
  • 函数的引用
  • 执行其它代码

我们通过下面例子来看看这是变量对象的变化。

var a = 1;
var f = true;
function f(){}
var cf = function(){}
b = 2;

// 伪代码
VO(globalContext) = {
    Math,
    Window:global,
    ... // 等等全局属性、函数或对象
    a:undefined, // 变量a被提升,并赋值为undefined
    cf:undefined, // 函数表达式cf 按变量处理
    f:<f> // 函数声明f被提升,覆盖同名变量f, 并指向函数f
}

上面的代码展示了进入执行上下文阶段时的变量对象。当进入执行阶段后,代码会按触发顺序从上向下去执行可执行代码。

  1. 当遇到var a = 1 时,变量对象中的a的值将不在是undefined,将会变为1
  2. 当运行到var f = true 时,变量对象中的f的值将不在是函数f,将会变为ture
  3. 当运行到function f(){} 时,函数f并不会在影响变量对象了。
  4. 当运行到var cf = function(){} 时,cf的值将不在是undefined,将会指向函数
  5. 当运行到b = 2; 时,会为全局对象添加b属性并赋值为2

通过上面的讲解,我们会很清楚为什么会出现下面这种情况:

console.log(a)  // undefined
console.log(f)  // 函数f

var a = 1;
var f = true;

console.log(a)  // 1
console.log(f)  // true

function f(){}

console.log(f)// true

参考文献

最近更新时间: 7/2/2021, 11:27:27 AM