js基础梳理-究竟什么是变量对象,什么是活动对象?
首先,回顾下上篇博文中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函数')}
总结一下,变量对象会有以下四种特性:
- 全局上下文的变量对象初始化是全局对象(其实这篇文章并没有介绍这个特性,不过它也很简单就这么一句话而已)
- 函数上下文的变量对象初始化只包括Arguments对象
- 在进入执行上下文的时候会给变量对象添加形参,函数声明,变量声明等初始的属性值
- 在代码执行阶段,会再次修改变量对象的属性值。
理解了这些,是不是发现再有一些函数提升,变量提升什么的是不是都很简单了。例如,你可以思考下这三段代码分别发生了什么。
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基础梳理-究竟什么是变量对象,什么是活动对象?的更多相关文章
- js基础梳理-究竟什么是执行上下文栈(执行栈),执行上下文(可执行代码)?
日常在群里讨论一些概念性的问题,比如变量提升,作用域和闭包相关问题的时候,经常会听一些大佬们给别人解释的时候说执行上下文,调用上下文巴拉巴拉,总有点似懂非懂,不明觉厉的感觉.今天,就对这两个概念梳理一 ...
- js基础梳理-关于this常见指向问题的分析
首先,依然回顾<js基础梳理-究竟什么是执行上下文栈(执行栈),执行上下文(可执行代码)?>中的 3.执行上下文的生命周期 3.1 创建阶段 生成变量对象(Variable object, ...
- js基础梳理-如何理解作用域和作用域链?
本文重点是要梳理执行上下文的生命周期中的建立作用域链,在此之前,先回顾下关于作用域的一些知识. 1.什么是作用域(scope)? 在<JavaScritp高级程序设计>中并没有找到确切的关 ...
- Js基础知识(一) - 变量
js基础 - 变量 *大家对js一定不会陌生,入门很简单(普通入门),很多人通过网络资源.书籍.课堂等很多途径学习js,但是有些js基础的只是往往被大家遗漏,本章就从js变量类型来说一说js 变量类型 ...
- 变量对象VO与活动对象AO
变量对象VO 变量对象VO是与执行上下文相关的特殊对象,用来存储上下文的函数声明,函数形参和变量.在global全局上下文中,变量对象也是全局对象自身,在函数上下文中,变量对象被表示为活动对象AO. ...
- js基础梳理-内存空间
我估计有很多像我这样非计算机专业的人进入到前端之后,总是在写业务代码,思考什么什么效果如何实现,导致很多基础概念型的东西都理解得并不太清楚.经常一碰到群里讨论的些笔试题什么的,总觉得自己像是一个假前端 ...
- JS基础-数据类型-运算符和表达式-变量和常量
1.js的基础语法2.js调试 1.F12调出控制台,查看提示错误及其位置. 2.出错时只影响当前代码块,不会影响其他代码块,后续代码块继续执行.3.语法规范 1.js语句:可执行的最小单元 必须以 ...
- js基础知识之_入门变量和运算符
js页面效果学习 (轮播图,文字滚动效果等等) javascript能来做什么 1.数据验证 2.将动态的内容写入网页中(ajax) 3.可以对时间做出响应 4.可以读写html中的内容 5.可以检测 ...
- JS基础_字面量和变量
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
随机推荐
- B - Finding Palindromes (字典树+manacher)
题目链接:https://cn.vjudge.net/contest/283743#problem/B 题目大意:给你n个字符串,然后问你将这位n个字符串任意两两组合,然后问你这所有的n*n种情况中, ...
- 转:Delphi中使用比较少的一些语法
http://www.cnblogs.com/Murphieston/p/5577836.html 本文是为了加强记忆而写,这里写的大多数内容都是在编程的日常工作中使用频率不高的东西,但是又十分重要. ...
- JQuery中的$.getScript()、$.getJson()和$.ajax()方法
$.getScript() 有时候,在页面初次加载时就取得所需的全部JavaScript文件是完全没有必要的.虽然可以在需要哪个JavaScript文件时,动态地创建<script>标签, ...
- Debian 9 源配置
Debian 9: deb http://mirrors.163.com/debian/ stretch main non-free contrib deb http://mirrors.163.co ...
- atof()函数 atol()
atof()函数 atof():double atof(const char *str ); 功 能: 把字符串转换成浮点数 str:要转换的字符串. 返回值:每个函数返回 double 值,此值由将 ...
- [转]AMBA、AHB、APB、ASB总线简介
[转]http://www.cnblogs.com/zhaozhong1989/articles/3092140.html 1.前言 随着深亚微米工艺技术日益成熟,集成电路芯片的规模越来越大.数字IC ...
- oracle的读写分离实现
在MySQL作为应用系统的后台数据库时,我们常常见到这样的架构,一拖二.一拖三等等.这是用MySQL的读写分离技术,实现数据的写入和读取分别在不同的库上,提升了数据库服务能力. 同样,在Oracle作 ...
- 【转】void及void指针的深刻解析
void的含义 void即“无类型” ,void*则为“无类型指针”,可以指向任何数据类型,所以又叫做“通用指针”. void指针使用规范 ①void指针可以只想任意类型的数据,亦即可用任意数据类型的 ...
- saltstack自动化运维系列⑤之saltstack的配置管理详解
saltstack自动化运维系列⑤之saltstack的配置管理详解 配置管理初始化: a.服务端配置vim /etc/salt/master file_roots: base: - /srv/sal ...
- mybatis二级缓存应用及与ehcache整合
mybaits的二级缓存是mapper范围级别,除了在SqlMapConfig.xml设置二级缓存的总开关,还要在具体的mapper.xml中开启二级缓存. 1.开启mybatis的二级缓存 在核心配 ...