执行上下文(作用域)
执行上下文
- 执行上下文:决定了变量和函数能访问哪些数据,以及它们的行为
- 上下文限制:每个上下文都有一个独立的变量对象,用于存储相关数据(递归)
- 上下文在其所有代码都执行完毕后会被销毁
- 全局上下文在应用程序退出前才会被销毁(关闭网页或退出浏览器)
- 上下文之间的连接是线性的、有序的。每个上下文都可以到上一级上下文中去搜索变量和函数,但任何上下文都不能到下一级上下文中去搜索(诞生闭包)
全局上下文
-
不同的宿主环境,全局上下文的对象可能也不同
- 在浏览器中,全局上下文是 window 对象
-
var
声明的全局变量会定义在全局对象上- 使用 let 和 const 的声明的全局变量不会定义在全局对象上
-
没有声明,只有赋值的变量会定义在全局上下文中
函数上下文
-
函数参数被认为是当前上下文中的变量
-
if
、for
等循环判断语句,没有上下文限制(坑)function fn(){ console.log(a);//undefined if(false){ var a=10; 没有上下文限制,被提升了 } }
-
在使用循环时,会污染上下文(坑)
- 解决:套一层作用域(立即执行函数)
var i = 99; 因为没有上下文限制,所以会污染 for (var i = 0; i < 5; i++) { console.log(i); } console.log(i);//5
实际代码相当于 var i = 99; { var i=0; } { var i=1; } { var i=2; } { var i=3; } { var i=4; } console.log(i);//5
利用立即执行函数提供函数上下文限制 var i = 99; (function () { for (var i = 0; i < 5; i++) { } })() console.log(i);//99
-
循环内为异步时因为没有上下文限制,所以没有对象去保存相关信息
- 解决:在内部使用立即执行函数,施加函数上下文
for (var i = 0; i < 5; i++) { setTimeout(() => { console.log(i); }, 500); } // 5 5 5 5 5
for (var i = 0; i < 5; i++) { 立即执行函数提供函数上下文,所以有相关对象去保存信息 (function (i) { setTimeout(() => { console.log(i); }, 500); })(i) } // 0 1 2 3 4
块级上下文
-
块级上下文是函数上下文的升级版本
-
只有
let
、const
声明的变量,才具有块级上下文 -
识别任何
{}
,使其拥有上下文限制- 在使用循环时,不会污染全局作用域
var i = 99; for (let i = 0; i < 5; i++) {} console.log(i);//99
因为块级上下文限制,不会对外产生影响
var i = 99; { let i=0; } { let i=1; } { let i=2; } { let i=3; } { let i=4; } console.log(i);//99
垃圾回收
- 思路:确定哪个变量不会再使用,然后释放它占用的内存
- 这个过程是周期性的,每隔一定时间就会自动运行
- 主要的标记策略:标记清理和引用计数(淘汰)
标记清理
- 当变量进入上下文,会被加上存在于上下文中的标记
- 当变量离开上下文时,会被加上离开上下文的标记
引用计数
- 声明变量并给它赋一个引用值时,这个值的引用数为 1
- 如果同一个值又被赋给另一个变量,那么引用数加 1
- 如果保存对该值引用的变量被其他值给覆盖了,那么引用数减 1
- 当一个值的引用数为 0 时,就说明没办法再访问到这个值了,因此可以安全地收回其内存了
- 严重的问题:循环引用
- 两个变量相互引用,它们的引用数永远不会变成 0
内存优化
-
保证在执行代码时只保存必要的数据。如果数据不再必要,那么把它设置为 null ,因为相关的值已经不在上下文里了,在下次垃圾回收时会被回收。
globalPerson = null;