首先,回顾下上篇博文中js基础梳理-究竟什么是执行上下文栈(执行栈),执行上下文(可执行代码)?的执行上下文的生命周期:

3.执行上下文的生命周期

3.1 创建阶段

  • 生成变量对象(Variable object, VO)
  • 建立作用域链(Scope chain)
  • 确定this指向

3.2 执行阶段

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

1.什么是变量对象(Variable Object)

在写程序的时候会定义很多变量和函数,那js解析器是如何找到这些变量和函数的?

变量对象是与执行上下文对应的概念,在执行上下文的创建阶段,它依次存储着在上下文中定义的以下内容:

1.1 函数的所有形参(如果是函数上下文中):

建立arguments对象。检查当前上下文中的参数,建立该对象下的属性与属性值。没有实参的话,属性值为undefined。

1.2. 所有函数声明:(FunctionDeclaration, FD)

检查当前上下文的函数声明,也就是使用function关键字声明的函数。在变量对象中以函数名建立一个属性,属性值为指向该函数所在内存地址的引用。如果变量对象已经存在相同名称的属性,则完全替换这个属性。

1.3. 所有变量声明:(var, VariableDeclaration)

检查当前上下文中的变量声明,每找到一个变量声明,就在变量对象中以变量名建立一个属性,属性值为undefined。如果变量名称跟已经声明的形式参数或函数相同,则变量声明不会干扰已经存在的这类属性。

2.什么是活动对象?(activation object, AO)

  • 只有全局上下文的变量对象允许通过VO的属性名称来间接访问,在其他上下文(后面干脆直接讲函数上下文吧,我们并没有分析eval上下文)中是不能直接访问VO对象的。

  • 在函数上下文中,VO是不能直接访问的,此时由活动对象AO继续扮演VO的角色。

未进入执行阶段前,变量对象中的属性都不能访问!但是进入到执行阶段之后,变量对象转变成了活动对象,里面的属性都能被访问了,然后开始进行执行阶段的操作。

因此,对于函数上下文来讲,活动对象与变量对象其实都是同一个对象,只是处于执行上下文的不同生命周期。不过只有处于执行上下文栈栈顶的函数执行上下文中的变量对象,才会变成活动对象。

3.举个例子

说了一堆概念,有点懵,对吗?请看这个例子:

var a = 10;
function b () {
console.log('全局的b函数')
};
function bar(a, b) {
console.log('1', a, b)
var a = 1
function b() {
console.log('bar下的b函数')
}
console.log('2', a, b)
}
bar(2, 3)
console.log('3', a, b)

要想知道为什么会这样打印,首先,从执行上下文的创建阶段来分析变量对象:

// 创建阶段:
// 第一步,遇到了全局代码,进入全局上下文,此时的执行上下文栈是这样
ECStack = [
globalContext: {
VO: {
// 根据1.2,会优先处理全局下的b函数声明,值为该函数所在内存地址的引用
b: <reference to function>,
// 紧接着,按顺序再处理bar函数声明,此时根据1.1,因为是在全局上下文中,并不会分析bar函数的参数
bar: <refernce to function>,
// 根据1.3,再处理变量,并赋值为undefined
a: undefined
}
}
];
// 第二步,发现bar函数被调用,就又创建了一个函数上下文,此时的执行上下文栈是这样
ECStack = [
globalContext: {
VO: {
b: <reference to function b() {}>,
bar: <refernce to function bar() {}>,
a: undefined
}
},
<bar>functionContext: {
VO: {
// 根据1.1,优先分析函数的形参
arguments: {
0: 2,
1: 3,
length: 2,
callee: bar
},
a: 2,
// b: 3,
// 根据1.2, 再分析bar函数中的函数声明b,并且赋值为b函数所在内存地址的引用, 它发现VO中已经有b:3了,就会覆盖掉它。因此上面一行中的b:3实际上不存在了。
b: <refernce to function b() {}>
// 根据1.3,接着分析bar函数中的变量声明a,并且赋值为undefined, 但是发现VO中已经有a:2了,因此下面一行中的a:undefined也是会不存在的。
// a: undefined
}
}
]

以上就是执行上下文中的代码分析阶段,也就是执行上下文的创建阶段。再看看执行上下文的代码执行阶又发生了什么。

// 执行阶段:
// 第三步:首先,执行了bar(2, 3)函数,紧接着,在bar函数里执行了console.log('1', a, b)。全局上下文中依然还是VO,但是函数上下文中VO就变成了AO。并且代码执行到这,就已经修改了全局上下文中的变量a.
ECStack = [
globalContext: {
VO: {
b: <reference to function b() {}>,
bar: <refernce to function bar() {}>,
a: 10,
}
},
<bar>functionContext: {
AO: {
arguments: {
0: 2,
1: 3,
length: 2,
callee: bar
},
a: 2,
b: <refernce to function b() {}>
}
}
] // 因此会输出结果: '1', 2, function b() {console.log('bar下的b函数')}; // 第四步:执行console.log('2', a, b)的时候, 发现里面的变量a被重新赋值为1了。
ECStack = [
globalContext: {
VO: {
b: <reference to function b() {}>,
bar: <refernce to function bar() {}>,
a: 10,
}
},
<bar>functionContext: {
AO: {
arguments: {
0: 2,
1: 3,
length: 2,
callee: bar
},
a: 1,
b: <refernce to function b() {}>
}
}
]
// 因此会输出结果: '2', 1, function b() {console.log('bar下的b函数')}; // 第五步,执行到console.log('3', a, b)的时候,ECStack发现bar函数已经执行完了,就把bar从ECStack给弹出去了。此时的执行上下文栈是这样的。 ECStack = [
globalContext: {
VO: {
b: <reference to function b() {}>,
bar: <refernce to function bar() {}>,
a: 10,
}
}
] // 因此会输出结果: '3', 10, function b() {console.log('全局的b函数')}

总结一下,变量对象会有以下四种特性:

  1. 全局上下文的变量对象初始化是全局对象(其实这篇文章并没有介绍这个特性,不过它也很简单就这么一句话而已)
  2. 函数上下文的变量对象初始化只包括Arguments对象
  3. 在进入执行上下文的时候会给变量对象添加形参,函数声明,变量声明等初始的属性值
  4. 在代码执行阶段,会再次修改变量对象的属性值。

理解了这些,是不是发现再有一些函数提升,变量提升什么的是不是都很简单了。例如,你可以思考下这三段代码分别发生了什么。

foo()
var foo = function() {console.log(1)}
function foo() {console.log(2)}
foo()
function foo() {console.log(2)}
var foo = function() {console.log(1)}
var foo = function() {console.log(1)}
function foo() {console.log(2)}
foo()

js基础梳理-究竟什么是变量对象,什么是活动对象?的更多相关文章

  1. js基础梳理-究竟什么是执行上下文栈(执行栈),执行上下文(可执行代码)?

    日常在群里讨论一些概念性的问题,比如变量提升,作用域和闭包相关问题的时候,经常会听一些大佬们给别人解释的时候说执行上下文,调用上下文巴拉巴拉,总有点似懂非懂,不明觉厉的感觉.今天,就对这两个概念梳理一 ...

  2. js基础梳理-关于this常见指向问题的分析

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

  3. js基础梳理-如何理解作用域和作用域链?

    本文重点是要梳理执行上下文的生命周期中的建立作用域链,在此之前,先回顾下关于作用域的一些知识. 1.什么是作用域(scope)? 在<JavaScritp高级程序设计>中并没有找到确切的关 ...

  4. Js基础知识(一) - 变量

    js基础 - 变量 *大家对js一定不会陌生,入门很简单(普通入门),很多人通过网络资源.书籍.课堂等很多途径学习js,但是有些js基础的只是往往被大家遗漏,本章就从js变量类型来说一说js 变量类型 ...

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

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

  6. js基础梳理-内存空间

    我估计有很多像我这样非计算机专业的人进入到前端之后,总是在写业务代码,思考什么什么效果如何实现,导致很多基础概念型的东西都理解得并不太清楚.经常一碰到群里讨论的些笔试题什么的,总觉得自己像是一个假前端 ...

  7. JS基础-数据类型-运算符和表达式-变量和常量

    1.js的基础语法2.js调试 1.F12调出控制台,查看提示错误及其位置. 2.出错时只影响当前代码块,不会影响其他代码块,后续代码块继续执行.3.语法规范 1.js语句:可执行的最小单元 必须以 ...

  8. js基础知识之_入门变量和运算符

    js页面效果学习 (轮播图,文字滚动效果等等) javascript能来做什么 1.数据验证 2.将动态的内容写入网页中(ajax) 3.可以对时间做出响应 4.可以读写html中的内容 5.可以检测 ...

  9. JS基础_字面量和变量

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

随机推荐

  1. AQS框架

    java并发包基石 AQS是JUC中很多同步组件的构建基础,简单来讲,它内部实现主要是状态变量state和一个FIFO队列来完成,同步队列的头结点是当前获取到同步状态的结点,获取同步状态state失败 ...

  2. SpringBoot整合MyBatis(XML)

    (1).添加依赖 <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId> ...

  3. Linux性能分析的前60000毫秒【转】

    Linux性能分析的前60000毫秒 为了解决性能问题,你登入了一台Linux服务器,在最开始的一分钟内需要查看什么? 在Netflix我们有一个庞大的EC2 Linux集群,还有非常多的性能分析工具 ...

  4. How to scroll down with Phantomjs to load dynamic content

    page.open('http://example.com/?q=houston', function () { // Checks for bottom div and scrolls down f ...

  5. jvm系列五、jvm垃圾回收机制、jvm各种参数及调优

    转载自:http://yufenfei.iteye.com/blog/1746914 尊重原创. 一.GC有两种类型:Scavenge GC 和Full GC 1.Scavenge GC 一般情况下, ...

  6. 基于内容的图片检索CBIR简介

    原文地址:http://blog.csdn.net/davebobo/article/details/53171311 传统的图像检索过程,先通过人工对图像进行文字标注,再利用关键字来检索图像,这种依 ...

  7. RestTemplate的使用

    1.postForObject :传入一个业务对象,返回是一个String 调用方: BaseUser baseUser=new BaseUser(); baseUser.setUserid(user ...

  8. HashMap Hashtable LinkedHashMap 和TreeMap

    Map主要用于存储健值对,根据键得到值,因此不允许键重复(重复了覆盖了),但允许值重复.Hashmap 是一个最常用的Map,它根据键的HashCode值存储数据,根据键可以直接获取它的值,具有很快的 ...

  9. C++ one more time

    写在前面:我们学习程序设计的方法先是模仿,然后举一反三.在自己的知识面还没有铺开到足够解决本领域的问题时,不要将精力过分集中于对全局无足轻重的地方!!! 以下参考钱能老师的<C++程序设计教程 ...

  10. 【linux】ubuntu下crontab无效解决方法

    在Debain的docker中启用crontab,踩了一整天的坑,特地记录一下.Debain和ubuntu差不多,故算在ubuntu下面了. 1.第一个坑,安装crontab apt-get inst ...