深入理解JavaScript 变量对象

 更新时间:2022年05月23日 08:31:19   作者:小宝的挖机  
变量对象是与执行上下文相关的数据作用域,存储了在上下文中定义的变量和函数声明,本文主要介绍了JavaScript 变量对象,具有一定的参考价值,感兴趣的可以了解一下

前言

在上节《深入 JavaScript 执行上下文栈——Web 前端进阶系列第三节》我们讲到,JavaScript 引擎执行一段可执行代码时,会创建对应的执行上下文。

对于每个执行上下文,都有三个重要属性:

  • 变量对象(Variable object,VO)
  • 作用域链(Scope chain)
  • this

今天我们来重点讲解变量对象。

变量对象

变量对象是与执行上下文相关的数据作用域,存储了在上下文中定义的变量和函数声明。

执行上下文分为两种:全局上下文和函数上下文,接下来我们来分别讲解这两种上下文的变量对象。

全局上下文中变量对象

全局上下文中的变量对象是全局对象。

下面我们来了解一下全局对象,在 W3school 中的介绍有:

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

  • 在顶层 JavaScript 代码中,可以用关键字 this 引用全局对象。全局对象在作用域链最底端,这意味着所有非限定性的变量和函数名都会作为该对象的属性来查询。

  • 由于全局对象在作用域链最底端,这也意味着在顶层 JavaScript 代码中声明的变量都将成为全局对象的属性。

字面上大家理解起来可能比较抽象,接下来我们结合具体例子作进一步讲解。

  • 在顶层 JavaScript 代码中,可以用关键字 this 引用全局对象。在浏览器 JavaScript 中,全局对象是 window。在 node.js 中,全局对象是 global。
console.log(this); // window
console.log(this === window); // true
  • 全局对象是 JavaScript 的全局函数和全局属性的占位符。在顶层 JavaScript 代码中声明的变量都将成为全局对象的属性。
// 声明的变量成为了全局对象的属性
var a = 1;
console.log(this.a); // 1

// 声明的函数成为了全局对象的属性
function b() {}
console.log(this.b); // function b
  • 通过使用全局对象,可以访问全局函数和全局属性,也可以访问所有其他预定义的对象、函数和属性。
// 使用全局对象访问全局属性 Math,它是一个对象,它拥有 random 方法。
console.log(this.Math.random()); // 打印一个随机数
  • 所有非限定性的变量和函数名都会作为该对象的属性来查询。
// 这里的 Math 是非限定性的函数名
console.log(Math.random()); // 打印一个随机数
  • 全局对象是 Object 构造函数的实例,这也意味着 Object.prototype(原型)上预定义的属性和方法,是可以通过全局对象访问到的。
console.log(this instanceof Object); // true
  • 在浏览器 JavaScript 中,全局对象有 window 属性且指向自身。
console.log(this.window === this); // true

函数上下文中的变量对象

在函数上下文中,我们用活动对象(activation object, AO)来表示变量对象。

活动对象和变量对象其实是一个东西,只是变量对象是规范上的或者说是引擎实现上的,不可在 JavaScript 环境中访问,只有到当进入一个执行上下文中,这个执行上下文的变量对象才会被激活,所以才叫 activation object,而只有被激活的变量对象,也就是活动对象,各种属性和方法才能被访问。

活动对象是在进入函数上下文时被创建的,它有函数的 arguments 属性作为初始化属性。arguments 属性的值就是 Arguments 对象。

执行过程

函数上下文的代码执行过程共分成两个阶段,分别是:预编译和执行。

预编译

  • 创建 AO 对象,寻找形参和变量声明

  • 把形参和变量名作为 AO 对象的属性名,值为 undefined

  • 把实参赋给形参,实参形参相统一

  • 寻找函数声明,值为函数体

我们来看个例子:

function foo(a) {
  var b = 2;
  function c() {}
  var d = function() {};

  b = 3;
}

foo(1);

这个函数在预编译完成后,AO 会变为:

AO = {
    arguments: {
        0: 1,
        length: 1
    },
    a: 1,
    b: undefined,
    c: reference to function c(){},
    d: undefined
}

代码执行

在代码执行阶段,会顺序执行代码。根据代码,修改变量对象的值。

上面的例子当代码执行完,AO 会变为:

AO = {
    arguments: {
        0: 1,
        length: 1
    },
    a: 1,
    b: 3,
    c: reference to function c(){},
    d: reference to FunctionExpression "d"
}

总结

至此,变量对象的创建过程我们就介绍完了,我们来做个总结:

  • 全局上下文的变量对象初始化是全局对象
  • 函数上下文的变量对象初始化只包括 Arguments 对象
  • 在进入执行上下文时会给变量对象添加形参、变量声明、函数声明等初始的属性值(预编译)
  • 在代码执行阶段,会修改变量对象的属性值

练习题

  • 第一题

来看下面两端代码,分别会打印什么?

function foo() {
  console.log(a);
  a = 1;
}

foo();
function bar() {
  a = 1;
  console.log(a);
}
bar();

第一段会报错:Uncaught ReferenceError: a is not defined。

第二段会打印:1。

因为第一段代码 a 没有变量声明,所以函数执行上下文的 AO 中没有 a 变量的定义,此时 AO 的值是:

AO = {
    arguments: {
        length: 0
    }
}

执行打印时,在函数执行上下文的 AO 中没有找到 a 变量的定义,然后就会去全局上下文中找,发现全局也没有,所以就会报未定义的错。

第二段代码,没有使用 var 关键字声明的变量会成为全局对象的属性,所以执行打印时,会从全局对象找到 a 的值,所以会打印 1。

  • 第二题
console.log(foo);

function foo() {}

var foo = 1;

会打印 foo 函数,而不是 undefined。

因为在预编译的第 4 步,会寻找函数声明,值为函数体,也就是函数声明会覆盖变量声明。

到此这篇关于深入理解JavaScript 变量对象的文章就介绍到这了,更多相关JavaScript 变量对象内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 如何使用原生Js实现随机点名详解

    如何使用原生Js实现随机点名详解

    这篇文章主要给大家介绍了关于如何使用原生Js实现随机点名的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-01-01
  • js的Object.assign用法示例分析

    js的Object.assign用法示例分析

    这篇文章主要介绍了js的Object.assign用法,结合实例形式分析了js Object.assign基本功能、原理、使用方法及相关操作注意事项,需要的朋友可以参考下
    2020-03-03
  • 浅谈JavaScript的函数及作用域

    浅谈JavaScript的函数及作用域

    函数和作用域是JavaScript的重要组成部分,我们在使用JavaScript编写程序的过程中经常要用到这两部分内容,借助此文一起来巩固下学习的内容吧。
    2016-12-12
  • 基于bootstrap实现广告轮播带图片和文字效果

    基于bootstrap实现广告轮播带图片和文字效果

    这篇文章主要介绍了基于bootstrap实现广告轮播带图片和文字效果,效果非常棒,需要的朋友可以参考下
    2016-07-07
  • JS+HTML5 FileReader实现文件上传前本地预览功能

    JS+HTML5 FileReader实现文件上传前本地预览功能

    这篇文章主要为大家详细介绍了JS+HTML5 FileReader实现文件上传前本地预览功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-07-07
  • javascript截取字符串(通过substring实现并支持中英文混合)

    javascript截取字符串(通过substring实现并支持中英文混合)

    用js方法substring()、方法substr()实现如标题所示的截取字符串并支持中英文混合,具体代码如下,感兴趣的各位可以参考下哈
    2013-06-06
  • 利用函数的惰性载入提高javascript代码执行效率

    利用函数的惰性载入提高javascript代码执行效率

    在 addEvent 函数每次调用的时候都要走一遍,如果浏览器支持其中的一种方法,那么他就会一直支持了,就没有必要再进行其他分支的检测了
    2014-05-05
  • js中的闭包实例展示

    js中的闭包实例展示

    闭包是js中的一大特色,也是一大难点。下面这篇文章主要给大家介绍了关于js中闭包的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用js具有一定的参考学习价值,需要的朋友们下面来一起看看吧
    2018-11-11
  • 区分中英文字符的两种方法(正则和charCodeAt())

    区分中英文字符的两种方法(正则和charCodeAt())

    最近在为项目写登录注册系列页面, 表单验证无疑是不可缺少的部分, 在这个jQ插件满天飞的web年代, 表单验证倒也不是难事. 但再好的插件, 也并不能做到十全十美
    2010-11-11
  • JavaScript cookie原理及使用实例

    JavaScript cookie原理及使用实例

    这篇文章主要介绍了JavaScript cookie原理及使用实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-05-05

最新评论