JS引擎对代码解析执行的过程,有两个阶段。
第一个阶段是构建语法树
。
第二个阶段是对语法树进行解析执行
。
第一阶段:构建语法树
构建过程按下不表,日后填坑。
若语法树无法构造,报语法错误。
第二阶段:解析执行
解析执行有三阶段:创建,执行,执行完毕。
解析执行三个阶段创建阶段
创建阶段,创建什么?
创建执行上下文(Execution context)
。
创建执行上下文
这个过程,也叫预解析(preparse)
.
预解析(preparse)
的内容如下:
1. 创建变量对象(Variable Object)
2. 建立作用域链(Scope chain)
3. 确定this引用
1. 创建变量对象(Variable Object)
变量对象是一个对象。
变量对象的属性有函数形参(arguments)
,变量声明(variables)
,函数声明(functions)
。
创建变量对象的过程
示例代码1:
function fn(a){
console.log(a);
var a = 123;
console.log(a);
function a(){};
console.log(a);
var b = function(){};
console.log(b);
function d(){};
}
//调用函数
fn(1);
执行fn(1)
时,对fn
预解析的创建变量对象
过程如下:
- 创建一个空对象
vo:{
// 空对象
}
- 形参变实参
// 找形参
vo:{
a : undefined,
}
// 形参变实参
vo:{
a : 1,
}
- 找变量声明
vo:{
a : undefined,
b : undefined
}
- 找函数声明
vo:{
a : function a(){},
b : undefined,
d : function d(){}
}
2. 建立作用域链(Scope chain)
作用域链是什么?是一个数组。
数组内容是父变量对象
。
在创建阶段
作用域链就这么点内容。
举个例子:
function foo() {
function bar() {
}
}
foo();
创建阶段时,foo的作用域链为
foo.[[scope]] = [
globalContext.VO
];
属性[[scope]]
即foo函数的作用域链。globalContext.VO
是指全局上下文的变量对象
执行foo函数。也就是bar函数创建阶段,bar的作用域链为
bar.[[scope]] = [
fooContext.AO,
globalContext.VO
];
而此时foo的作用域链为
foo.[[scope]] = [
[AO],
globalContext.VO
];
这里的[AO]
是指foo函数的活动对象(Active Object)。本质就是foo函数的变量对象(Variable Object)。
同一个东西不同阶段的称呼而已。
闭包
每个函数都有作用域链,引用了父活动对象或全局变量对象。
所以,每个函数都是闭包。
因为,闭包的定义是引用了自由变量的函数。
3. 确定this指向
创建阶段的this指向undefined
。
执行阶段
1.修改活动对象。
2.遇到异步代码,涉及到macro-task(宏任务)
,micro-task(微任务)
。
3.赋值操作,进行内存分配。
this的变化
谁调用函数,该函数的执行上下文的this指向谁。
函数调用场景:
- new调用
- 对象调用
- 系统调用
执行完毕阶段
垃圾回收策略有两个,标记清除,引用计数(已淘汰)。
标记清除策略:
(1)创建阶段对当前执行上下文的变量进行标记。
(2)执行阶段,执行到所需变量的操作时,去掉它的标记。
(3)执行完毕阶段,有标记的变量就被视为准备删除的变量。
(4)垃圾收集器销毁带标记的变量,回收它们所占用的内存空间。
引用计数被淘汰的原因是循环引用
会导致垃圾回收失败。
垃圾回收策略对写代码的参考:
参考:
Google V8 的垃圾回收引擎
聊聊V8引擎的垃圾回收
参考:
https://my.oschina.net/ffwcn/blog/209465
https://juejin.im/post/5aa6693df265da23884cb571
https://www.cnblogs.com/ivehd/p/executionContext.html
网友评论