这是我见过描述的最为详尽的关于变量对象、活动对象以及闭包的解析,来自知乎,感谢答主:

作者:闭家锁
链接:https://www.zhihu.com/question/36393048/answer/71879330
来源:知乎
著作权归作者所有,转载请联系作者获得授权。

在回答这道题的时候,我也是抱着总结的目的来学习的,因为遇到了某些疑问所以最近一直在温习闭包、执行环境与作用域以及function引用类型的知识。

鉴于题主没有问题描述,那么我姑且认为题主具有初级以上的JS语法知识熟悉作用域和执行环境乃至闭包,我也认为题主应该知道变量对象就是执行环境中包含了所有变量函数的对象。另外变量对象是后台的,保存在内存中的,代码无法直接访问的。

假设在全局环境下定义了函数pub()和变量pubvar:

var pubvar = 1;
function pub () {
var pravar = 2;
return pubvar + pravar;
} alert(pub(2)); //调用pub()函数

此时后台会创建一个作用域链(scope chain),这个作用域链包含了全局环境的变量对象并被保存在pub()函数内部的scope属性中。但是,当我们打开浏览器的时候已经存在了一个全局的执行环境,这个全局的执行环境属于浏览器,JS里浏览器被称为window对象,我们把这个环境叫做A环境,只要没有关闭浏览器,A环境会一直存在下面会提到执行环境什么时候会被创建。
我们用色块表示执行环境,链条表示作用域链,作用域链上半部分是活动对象区域,下半部分是变量对象区域,如下图:

当我们要调用pub()函数的时候,又会创建一个执行环境B,没错,执行环境如其名是在运行和执行代码的时候才存在的,所以我们运行浏览器的时候会创建全局的执行环境。这个时候根据pub()函数scope属性中的作用域链把pub()函数内的变量对象放入新的B环境中,作用域链也得到更新,如下图:

上图的虚线表示正在执行,全局变量对象此时处于作用域链的第二位,所以标号变成了1。你可能也注意到那个arguments对象,它是在函数被创建的时候就一直存在的,无需用户创建。arguments对象保存的是函数圆括号内定义的参数,准确来说保存的是参数的值,因这里我们没有设置参数,所以显示未定义。

此时我们把属于B环境的变量对象(也就是pub()函数中的所有函数和变量)叫做活动对象。

因此我们可以说变量对象包含了活动对象,活动对象就是作用域链上正在被执行和引用的变量对象。我们从活动对象的名称中也能看出 “执行、运行、激活” 等意味。你可以这样理解,整个代码的运行总有一个起始的对象吧,不管这个起始是变量还是函数,总要有一个称呼,虽然我们把执行环境中的变量和函数的总称叫做变量对象,但这不能反映代码的动态性,为了区别于普通的变量对象,我们创造了活动对象的概念。

这就完了?没有,上一段我们说到代码的动态性,所谓动态性在这里的意思是有些代码参与了运行,有些没有,就像高中的化学反应一样,总有一些化学物不会参与到整个反应中来。我们把上面的代码变成如下:

var pubvar = 1;
function pub () {
var pravar = 2;
return pubvar + pravar;
} var pubvar2 = 3;
function pub2 () {
var pravar = 2;
return pubvar2 + pravar;
} alert(pub(2)); //调用pub()函数

这个时候全局作用域链和执行环境如下:

接着我们调用pub()函数,执行环境和作用域链如下:

你看没有被调用的pub2()函数仍然只是闲着,甚至没有被pub()函数在内部引用。由于pub2()没有参与整个pub()函数的调用过程,所以pub2()中不存在活动对象,只有“处于静止状态”的变量对象,当然也没有创建执行环境。

到这里就完了?也没有,我们刚才也只是讨论了两个平行且毫不关联的函数其中一个被调用的状况,言下之意就是也存在函数相互影响的例子,最典型的就是闭包,闭包是一种函数嵌套的情况。
定义如下代码:

function returnfunc (propertyName) {
return function (obj) { //-----这行定义并返回了一个闭包,也被称之为一个匿名函数
return obj[propertyName]; //这里用方括号法访问属性,因为属性是变量(returnfunc()函数的参数)
};
} var savefunc = returnfunc("name"); //调用returnfunc()
var result = savefunc({name:"Picasso"});//调用savefunc()
alert(result); //返回字符串“Picasso”

以上代码的最开始的作用域链和执行环境:

我们先开始调用returnfunc()函数,马上会创建一个包含returnfunc()变量对象的行环境,作用域链开始变化,如下图:

图的白色虚线表示执行程序产生的效果,它可能表示的是返回一个结果、复制某种值、产生一个新物体、建立某种联系等。
题外话:你会发现上图的arguments参数的值和propertyName的值是一样的,这是因为arguments保存的就是参数,采用实时映射的方式与参数建立联系,如果你在returnfunc()函数内再加一个值为{name:"picasso"},名为obj的参数,那么arguments的值变成[{name:"nicholas"},{name:picasso}],是的,你没看错,arguments是一个数组!!!

随后returnfunc()函数会返回它内部的匿名函数,当匿名函数被返回后,整个作用域链和执行环境又发生了变化:

我们看到匿名函数(闭包)被添加到了最作用域链的最前端,returnfunc()的执行环境被销毁,但我们注意到returnfunc()函数的活动对象仍然在被引用(匿名函数仍在访问propertyName参数),因此returnfunc()函数的变量对象仍然在内存中,成为活动对象。这就是为什么匿名函数就能访问returnfunc()函数定义的所有变量和全局环境定义的变量,毕竟returnfunc()的活动对象仍然保持“激活”状态。

根据上面所述,随着代码一行一行的被执行、执行环境不断被创建和销毁、变量对象间的各种关系被建立,这些背后的逻辑导致活动对象也在不断变化,这足以证明活动对象只是正在被执行和引用的变量对象。

我个人的理解是:活动对象是按照状态划分的一类变量对象,你也可以根据变量对象被调用次数的多少来定义一个常用对象和闲置对象,不过没人这么蛋疼。。。

变量对象vs活动对象的更多相关文章

  1. 图解Javascript——变量对象和活动对象

    span { line-height: 1.5 } 这是由一段代码引发的思考: var laterDeclaredVar = 'I am a global variable ...'; (functi ...

  2. javascript中函数的执行环境、作用域链、变量对象与活动对象

    javascript高级程序设计中:对执行环境.作用域链.变量对象.活动对象的解释: 1.执行环境: 执行环境:有时也叫环境:是JavaScript中最为重要的一个概念:执行环境定义了变量或函数有权访 ...

  3. 1--面试总结-js深入理解,对象,原型链,构造函数,执行上下文堆栈,执行上下文,变量对象,活动对象,作用域链,闭包,This

    参考一手资料:http://dmitrysoshnikov.com/ecmascript/javascript-the-core/中文翻译版本:https://zhuanlan.zhihu.com/p ...

  4. js中变量对象和活动对象的区别 闭包时的区别

    变量对象:就是执行环境中包含了所有的变量和函数的对象 活动对象:正在被执行或引用的变量和函数 在上述代码中 ,全局定义了函数compare()和变量result,后台会创建一个作用域链(scope c ...

  5. (O)JS:执行环境、变量对象、活动对象和作用域链(原创)

    var a=1; function b(x){ var c=2; console.log(x); } b(3); ·执行环境(execution context),也称为环境.执行上下文.上下文环境. ...

  6. 关于javascript中的变量对象和活动对象

    https://segmentfault.com/a/1190000010339180 https://zhuanlan.zhihu.com/p/26011572 https://www.cnblog ...

  7. 变量对象VO与活动对象AO

    变量对象VO 变量对象VO是与执行上下文相关的特殊对象,用来存储上下文的函数声明,函数形参和变量.在global全局上下文中,变量对象也是全局对象自身,在函数上下文中,变量对象被表示为活动对象AO. ...

  8. js基础梳理-究竟什么是变量对象,什么是活动对象?

    首先,回顾下上篇博文中js基础梳理-究竟什么是执行上下文栈(执行栈),执行上下文(可执行代码)?的执行上下文的生命周期: 3.执行上下文的生命周期 3.1 创建阶段 生成变量对象(Variable o ...

  9. javascript活动对象的理解——伪单例模式

    在自己研究javascript各种设计模式的过程中,偶然写出的一段代码让自己理解的更深刻了,之所以称之为伪单例模式,是因为这段代码造成的结果很想单例模式,但是实际上是活动对象捣乱所造成的误会. 代码很 ...

随机推荐

  1. 【Docker 命令】- login 命令

    docker login : 登陆到一个Docker镜像仓库,如果未指定镜像仓库地址,默认为官方仓库 Docker Hub docker logout : 登出一个Docker镜像仓库,如果未指定镜像 ...

  2. chrome extensions & debug

    chrome extensions & debug debug background.js debug popup.js debug content_script.js chrome.stor ...

  3. 第49天:封装自己的scrollTop

    一.scroll家族 offset 自己的偏移scroll滚动的 scrollTop和scrollLeftscrollTop 被卷去的头部当滑动滚轮浏览网页的时候,网页隐藏在屏幕上方的距离二.页面滚动 ...

  4. 【题解】Atcoder AGC#01 E-BBQ Hard

    计数题萌萌哒~ 这道题其实就是统计 \(\sum_{i=1}^{n}\sum_{j=i+1}^{n}C\binom{a[i] + a[j]}{a[i] + a[j] + b[i] + b[j]}\) ...

  5. 【题解】SCOI2008配对

    贪心+dp~观察数据,发现一个规律:将数字排序之后,最优匹配只可能产生在该数字和与它距离不超过二的数字之间. 所以可以用dp[i]代表前i个数(排序)匹配的最小差值,之后暴力选出该新数应该如何匹配. ...

  6. spring+springMVC+mybatis较全

    1.基本概念   1.1.Spring   Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson 在其著作Expert One-On- ...

  7. javascript实现deepEqual和shallowEqual

    function deepEqual(x, y) { if (x === y) { return true; } if (!(typeof x == "object" && ...

  8. oracle-java7-installer安装java失败之后的处理

    最开始尝试使用installer安装jdk7,但是未能进行完整,之后每次安装软件都会报错,说oracle-java7-installer处有错误,查得如下解决办法: sudo rm /var/lib/ ...

  9. 【状压DP】【UVA11795】 Mega Man's Mission

    传送门 Description 你要杀n个怪,每杀掉一个怪那个怪会掉落一种武器,这种武器可以杀死特定的怪.游戏初始你有一把武器,能杀死一些怪物.每次只能杀一只,求有多少种杀怪方法. Input 多组数 ...

  10. redis缓存在项目中的使用

    关于redis为什么能作为缓存这个问题我们就不说了,直接来说一下redis缓存到底如何在项目中使用吧: 1.redis缓存如何在项目中配置? 1.1redis缓存单机版和集群版配置?(redis的客户 ...