是什么
js引擎在执行代码前会做些准备工作:就是创建对应的执行上下文,它可以理解为代码块执行时的环境,这个环境中包含了代码块要用的变量、函数、this等。
js引擎会以栈的方式来处理这些环境:
进栈相当于进入代码执行的环境,出栈相当于离开这个环境。
这个栈被称为函数调用栈。
分类
执行上下文可分为三类:
- 全局上下文,代码一开始运行时必定会首先进入该环境
- 函数上下文,当函数被调用时,会创建该函数的执行上下文
- eval上下文,查阅很多都说不用管,不会用到,那就不管啦
组成及创建
由三部分组成:
- 变量对象,用于保存这个环境中的变量和函数声明
变量对象可以看JS变量对象详细解释 - 作用域链,是一个单向链表,表里面有未被销毁的变量对象
- this指针
执行过程
这里举例子更直观些:
function a() {
console.log('a')
function b() {
console.log('b')
}
b()
}
a()
最开始一定是创建全局上下文,并为它分配地址,然后进入函数调用栈
执行a(),创建a的全局上下文,并为它分配地址,上下文进入函数调用栈,
执行a时遇到b(),创建b的全局上下文,并为它分配地址,进入函数调用栈
b函数执行完,出栈,b的上下文没有被引用,被垃圾回收,然后继续执行a函数
a函数执行完,出栈,a的上下文没有被引用,被垃圾回收
当页面关闭时,全局上下文才出栈,失去引用,被垃圾回收
由此我们可以发现:
栈底永远是全局上下文,栈顶就是当前执行上下文。
要注意的是,每次函数被调用,就会为其创建新的执行上下文,即使是同一个函数。
基于此,我们来解释下闭包,先拿个例子
function a(){
var x =1
return function(){ //函数可以访问外部x
x++
console.log(x)
}
}
var add = a()
var add2 = a()
add()
add()
add2()
add2()
输出为:2,3,2,3
先是全局上下文入栈,然后a函数的执行上下文入栈,执行到return,出栈,这时我们把返回值保存了下来,变量add为a函数返回的匿名函数在内存中的地址引用,这就说明函数a的执行上下文中的东西仍然有被用到,所以并没有被垃圾回收,还保存在内存中,所以就还可以访问到其中变量。
因为函数每次调用都回创建新的执行上下文,所以add和add2可访问的x所在的上下文处在内存不同地址,所以不是同一个x。
用到闭包的地方很多,明天再写一篇总结归纳下吧。