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

御弟哥哥
• 阅读 1492

在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 
点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
2年前
Android So动态加载 优雅实现与原理分析
背景:漫品Android客户端集成适配转换功能(基于目标识别(So库35M)和人脸识别库(5M)),导致apk体积50M左右,为优化客户端体验,决定实现So文件动态加载.!(https://oscimg.oschina.net/oscnet/00d1ff90e4b34869664fef59e3ec3fdd20b.png)点击上方“蓝字”关注我
Stella981 Stella981
2年前
JS 对象数组Array 根据对象object key的值排序sort,很风骚哦
有个js对象数组varary\{id:1,name:"b"},{id:2,name:"b"}\需求是根据name或者id的值来排序,这里有个风骚的函数函数定义:function keysrt(key,desc) {  return function(a,b){    return desc ? ~~(ak
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
35岁是技术人的天花板吗?
35岁是技术人的天花板吗?我非常不认同“35岁现象”,人类没有那么脆弱,人类的智力不会说是35岁之后就停止发展,更不是说35岁之后就没有机会了。马云35岁还在教书,任正非35岁还在工厂上班。为什么技术人员到35岁就应该退役了呢?所以35岁根本就不是一个问题,我今年已经37岁了,我发现我才刚刚找到自己的节奏,刚刚上路。
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这