执行上下文,作用域和垃圾清理

本文最后更新于:2023年1月12日 下午

执行上下文,作用域和垃圾回收

执行上下文是当前JavaScript代码被解析和执行时所在环境的抽象概念。变量或函数的上下文决定了它们可以访问哪些数据。

执行上下文的类型

执行上下文包含全局执行上下文、函数执行上下文和eval执行上下文(不建议使用)

全局执行上下文:是最外层的上下文,在浏览器环境中就是全局对象windowthis指向这个全局对象。全局执行上下文仅此一个

函数执行上下文:当函数被调用时,会创建一个函数执行上下文并推到一个上下文栈,等到函数执行完,上下文栈会弹出该函数执行上下文。可以存在无数个,且每次调用创建的是一个新的执行上下文

eval函数执行上下文:指的是运行在eval函数中的代码

  • 所有通过var定义的全局变量和函数都会成为window对象的属性和方法。使用letconst的顶级声明不会定义在全局上下文中

  • 上下文在其所有代码都执行完毕后会被销毁

执行栈也叫调用栈,具有LIFO(后进先出)结构,用于存储在代码执行期间创建的执行上下文

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
let scope = 'local scope'
function test1() {
let scope = 'local scope'
function fn(){
return scope
}
return fn()
}
test1() //全局执行上下文入栈 => test1函数执行上下文入栈 => fn函数执行上下文入栈 => 执行完fn函数,fn函数执行上下文出栈 => test1函数执行上下文出栈 => 全局执行上下文出栈

function test2() {
let scope = 'local scope'
function fn() {
return scope
}
return fn
}
test2()() //全局执行上下文入栈 => test2函数执行上下文入栈 => test2函数执行上下文出栈 => fn函数执行上下文入栈 => fn函数执行上下文出栈 => 全局执行上下文出栈

//执行结果都一样,但是执行上下文栈变化不一样

作用域

当执行上下文中的代码在执行的时候,会创建变量对象的一个作用域链。这个作用域链决定了各级上下文所能访问到的变量和函数顺序。处在作用域顶端的执行上下文,叫做活动对象。活动对象最初只用arguments。作用域链的中的下一个变量对象来自于包含上下文,再下一个来自于在下一个包含上下文。全局执行上下文的变量对象始终是作用域链的最后一个变量对象。

垃圾回收

垃圾回收有两种策略,标记清除法和引用计数法。现阶段浏览器使用的多为标记清楚法。
引用计数无法清除循环引用。

1
2
3
4
5
6
7
8
function problem() {
let objectA = new Object()
let objectB = new Object()

objectA.someOtherObject = objectB
objectB.anotherObject = objectA
}
//objectA和objectB通过各自的属性相互引用,意味着引用数始终都为2

内存泄漏

  • 缺少声明关键字导致的内存泄漏
1
2
3
function test() {
name = 'yxlazy'
}

当调用test()时,会在全局对象window上添加name属性导致内存泄漏,只要window不被清理就不会消失

  • 定时器导致的内存泄漏
1
2
3
4
let name = 'yxlazy'
setInterval(() => {
console.log(name)
}, 1000)

定时器的回调通过闭包引用了外部变量。只要定时器一直运行,回调函数中引用的name就会一直占用内存

  • 使用JavaScript闭包造成的内存泄漏
1
2
3
4
5
6
let outer = function () {
let name = 'yxlazy'
return function () {
return name
}
}

调用outer()会导致分配给name的内存被泄漏


执行上下文,作用域和垃圾清理
https://www.yxlazy.xyz/2021/03/27/执行上下文,作用域和垃圾清理/
作者
yxlazy
发布于
2021年3月27日
更新于
2023年1月12日
许可协议