美文网首页H5游戏开发Web前端之路Web 前端开发
彻底理解js的执行上下文,以及变量对象

彻底理解js的执行上下文,以及变量对象

作者: csRyan | 来源:发表于2017-03-08 15:40 被阅读483次

在js中,执行上下文(Execution Context)是非常重要的一种对象,它保存着函数执行所需的重要信息,其中有三个属性:变量对象(variable object)作用域链(scope chain)this指针(this value),它们影响着变量的解析变量作用域函数this的指向


上下文栈(Execution Context Stack)

js执行的时候会维护一个上下文栈,每次开始执行一个函数之前,js都要创建一个上下文对象,并将其压入上下文栈中。因此,当前正在执行的函数的上下文(简称当前上下文)总是在栈顶,这个函数一执行完,上下文就会从栈中弹出。

代码开始运行时,栈顶会先放入一个全局上下文,全局上下文同样有变量对象,作用域链和this指针。

举个例子:

function fun2() {
    var b = 222;
}
function fun1() {
    var a = 111;
    fun2();
}
fun1();
c = 333;
  1. 开始执行时:
    栈顶:全局上下文
  2. 代码执行到fun1();时,创建fun1的上下文,压入栈。这时栈变成:
    栈顶:fun1上下文 / 全局上下文
  3. 代码执行到fun2();时,创建fun2的上下文,压入栈。这时栈变成:
    栈顶:fun2上下文 / fun1上下文 / 全局上下文
  4. fun2执行完毕,fun2上下文弹出:
    栈顶:fun1上下文 / 全局上下文
    (这时执行权回到fun1内,栈顶也恰好回到了fun1上下文。可见这种机制能保证正在执行的函数的上下文总是在栈顶)
  5. fun1执行完毕,fun1上下文弹出,执行权回到全局区域:
    栈顶:全局上下文
  6. 所有代码都执行完毕,全局上下文弹出,栈为空。

执行上下文.变量对象(Variable Object)

我们已经说过,每次执行(注意是执行而不是声明!)一个函数之前,执行引擎都会创建一个上下文对象。创建上下文对象的时候,就会创建它的一个重要属性:变量对象。
创建变量对象的过程是这样:

  1. 建立arguments对象:属性名是'0'、'1'、'2'.....,属性值就是实际传入的参数。此外arguments.length是实际参数的个数
  2. 找到这个将要执行的函数内的所有函数声明,储存在变量对象中,属性名就是函数名,属性值就是函数的引用(所在的内存地址)。如果有多个同名的函数声明,后出现的函数覆盖前面的属性值。
  3. 找到这个将要执行的函数内的所有变量声明,储存在变量对象中,属性名就是变量名,属性值是undefined

还有一个概念叫做活动对象(activation object),活动对象其实和变量对象是同一个东西在不同时期的两种叫法。函数还未开始执行(创建上下文的期间)时叫变量对象,函数开始执行以后就叫活动对象。


变量对象让js有变量提升的特性

原来,在js执行函数之前,会先扫描一遍代码,将变量、函数声明都放到变量对象中。当执行函数时如果遇到一个变量、函数名,就会到活动对象中去找,发现有对应的属性名,就可以从变量对象中取出它的属性值来使用,而不用等到那一句声明语句之后!
例子:

function fun1(var arg) {
    // 创建变量对象:{arg:987, fun2:fun2的地址, a:undefinded}
    console.log(a);  // 打印undefinded,因为活动对象中有键值对:a:undefinded。
    var a = 111;      // 如果将这一语句删除,上一句会直接报错!
    console.log(a);  // 打印111,因为活动对象中有键值对:a:111
    fun2();          // 打印in fun2! 因为活动对象中有键值对:fun2:某个内存地址
    return;          // 即使是在return之后的声明,也会被放入变量对象!
    function fun2() {
        console.log('in fun2!');
    }
}
fun1(987);
// 输出为:
// undefined
// 111
// in fun2!

为什么js不是块级作用域

这通过变量对象就可以解释了。因为只要在同一个函数中声明,所有变量都会保存在同一个变量对象中!所以在代码块中声明变量和在代码块外声明变量当然没有区别!


还记得我们刚才说“代码开始运行时,栈顶会先放入一个全局上下文”吗?在浏览器中,全局上下文的变量对象就是全局对象(就是window对象)!这就可以解释以下代码:

// 执行函数之前发现a的声明,将其加入window对象,设为undefined
var a = 111;  //  在活动对象(也就是window)中将a赋值为111
console.log(window.a);  // 打印111
window.a = 111;
console.log(a);  // 打印111,因为在活动对象(也就是window)中找到了a:111

经过测试,Node.js全局上下文的变量对象不是全局对象(global)。

var a = 111;
console.log(global.a);  // 打印undefined

有关作用域链的解析,请看我的这一篇文章

有关this指针是如何确定的,请看这一篇文章

相关文章

  • 如何确定js里的this

    在我的彻底理解js的执行上下文,以及变量对象中我说过,执行上下文是在函数被调用的时候被创建的,其中包括创建变量对象...

  • js 随笔

    js 执行机制: js执行上下文: 只有理解了js 执行上下文才能更好的理解 js变量提升以及 作用域和闭包 所谓...

  • 彻底理解js的执行上下文,以及变量对象

    在js中,执行上下文(Execution Context)是非常重要的一种对象,它保存着函数执行所需的重要信息,其...

  • 你不知道的js变量提升

    所谓的js变量提升,笔者理解为就是执行上下文所引出的概念。那么我们需要着重来理解执行上下文,执行上下文理解了,所谓...

  • 深入学习JS执行--创建执行上下文(变量对象,作用域链,this

    一、介绍 这次我们来深入了解js执行过程中的执行上下文。 本篇涉及到的名词:预执行,执行上下文,变量对象,活动对象...

  • JS Context

    js的执行上下文 在js中,执行上下文保存着函数执行所需的重要信息,其中包括了三个属性: 变量对象作用域链this...

  • 事件机制、执行上下文

    执行上下文 EC 理解:代码的执行环境 时机:代码正式执行之前会进入到执行环境 工作: 创建变量对象变量;函数及函...

  • 什么是变量提升?

    概念一:执行上下文; 概念二:变量对象; 概念三 ;内存空间的概念。 执行上下文,就是当前代码的运行环境。JS里的...

  • JS对象模型 - 执行模型

    对象 js的对象模型,执行上下文

  • 变量对象 - 2023-02-09

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

网友评论

  • 悟空弜厸:看了你的文章,发现一个问题arguments对象里面存的是实际参数吧!每个函数内部都有一个arguments变量里面存的也是实际参数,函数的length属性存的是形式参数
    悟空弜厸: @悟空弜厸 没事儿,没事儿! 你文章写的很好啊!
    csRyan:谢谢提醒!之前我写“arguments的属性名是形式参数名”不正确,实际上arguments的属性名是数字!
    我之前写的也是“属性值是实际传入的参数”,这一点没有错,因为arguments对象是在函数执行之前创建出来的嘛!
    arguments.length是实际参数的个数,函数名.length是形式参数的个数!

本文标题:彻底理解js的执行上下文,以及变量对象

本文链接:https://www.haomeiwen.com/subject/nshvgttx.html