执行上下文
v8在编译js的时候会经过:
-
词法分析
分析该js代码的代码块是否正确,如果不正确则向外抛出语法错误,组织该代码块的执行,然后继续查找下一个代码块,语法正确,则进入预编译阶段
-
预编译
创建变量对象(创建arguments对象,函数生命提前解析,变量生命提升)
js的运行环境:
- 全局环境(js代码加载完毕后,进入代码预编译即进入全局环境)
- 函数环境(函数调用执行时,进入函数环境,不同函数的函数环境不同)
- eval
每进入一个不同的运行环境即创建一个相应的
执行上下文,在一段js程序中会创建多个执行上下文
创建
执行上下文只要做了三件事:
(1)创建变量对象
- 创建arguments对象:检查当前上下文中的参数,建立该对象的属性和属性值(箭头函数和全局环境没有这一步)
- 检查当前上下文的函数声明,找到函数声明将其值指向函数所在堆地址的引用中
- 检查当前上下文的变量声明
函数声明和变量提升是在创建变量对象中进行的,且函数声明优先级高于变量声明
创建的变量对象发生在预编译极端,尚未执行,改变量对象是无法访问的,值仍未undefined,只有进入执行阶段,变量对象转换为活动对象后才能进行访问这就是VO(变量对象)----》AO(活动对象)的过程
(2)建立作用域链
作用域链由当前执行环境的变量对象与上层环境的一系列活动对象组成,保证了当前执行环境符合访问权限的变量和函数的有序访问
作用域链相当于一个数组,第一项是当前作用域,最后一项是全局作用域,作用域链保证了变量和函数的有序访问,沿着作用域从左向右查找变量或函数,找到就会停止查找,找不到就一直查找直到全局作用域,再找不到就会抛出错误。
闭包
- 函数嵌套函数
- 内部函数可以访问外部函数的局部变量
- 外层函数的局部变量不会被垃圾回收机制自动回收
(3)确定this指向
在全局执行上下文中this指向window;函数环境中的this根据执行环境和执行方法确定
-
执行
js引擎会以栈的方式对这些执行上下文进行处理,动态形成函数调用栈,栈底永远是全局执行上下文,栈顶永远是当前执行上下文;
function foo(){
let a = 1
let b = { name: 'cl' }
function showName(){
let c = 2
let d = { name: 'cl' }
}
showName()
}
foo()
堆数据回收
- V8中会把堆分为新生代和老生代两个区域,新生代中存放的是生存时间短的对象,老生代中存放的生存时间久的对象。
- 垃圾回收器,主要负责新生代的垃圾回收。
- 主垃圾回收器,主要负责老生代的垃圾回收。
新生代的垃圾回收Scavenge 算法:
- 新生代空间对半划分为两个区域,一半是对象区域,一半是空闲区域;
- 新加入的对免都会存放到对象区域,当对鱼区域快被写满时,就需要执行一次垃圾清理操作;
- 进行清理操作时,都需要将存活的对象从对象区域复制到空闲区域;
- 因为新生区的空间不大,所以很容易被存活的对象装满整个区域,为了解决这个问题,lavaScript引整采用了对象晋升策略,也就是经过两次垃圾回收依然还存活的对象,会被移动到老生区中;
老生代的垃圾回收标记-清除(Mark-Sweep)算法:
首先是标记过程阶段。标记阶段就是从一组根元素开始,递归遍历这组根元素,在这个遍历过程中,能到达的元素称为活动对象,没有到达的元素就可以判断为垃圾数据。
接下来就是垃圾的清除过程。标记-清除算法后,会产生大量不连续的内存碎片。
老生代的垃圾回收标记-整理(Mark-Compact)
首先是标记过程阶段。
让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。
欢迎关注我的前端自检清单,我和你一起成长