执行上下文/ 闭包/ 作用域链
# 执行上下文/ 闭包/ 作用域链
# 介绍一下闭包特别重要
概念
闭包是指有权访问另一个函数作用域里面的函数,最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量,利用闭包可以突破作用链域
用途:
通过闭包,可以在外部调用闭包函数 从而从外部调用函数内部的变量,创建私有变量
闭包另一个用途是使已经运行结束的函数上下文的变量对象继续留在内存中,因为闭包保留这个变量对象对象的引用,不会被回收
对于闭包的惰性解析
V8
的惰性解析是指解析器在解析过程中,如果遇到函数声明,则跳过函数内部代码,仅生成顶层代码的AST
和字节码。利用惰性解析可以加速JS
的编译速度和节约内存。
V8
解析函数的时候,会使用预解析器快速解析函数内部是否包含了外部函数声明的变量,如果引用了,就会将该变量复制存放到堆中,即使当前函数执行完也不会释放该变量,从而实现闭包的功能,如果没引用,则根本不会产生这个闭包。
# 对作用域 作用域链的理解特别重要
全局作用域
- 最外层函数及外面定义的变量拥有全局作用域
- 未定义直接赋值的变量拥有全局作用域
- 所有
window
对象属性拥有全局作用域 - 会污染全局命名空间,引起命名冲突
函数作用域
- 函数作用域声明在函数内部,一般只有固定代码片段可以访问到
- 内层作用域可以访问到外层,反之不行
块级作用域
let
const
可以声明块级作用域 可以在函数也可以在{}
里面- 循环中适合绑定块级作用域 可以将声明的计数器变量限制在循环内部
作用域链是 在当前作用域查找所需的变量,如果在自己的作用域找不到该变量就去父级作用域查找,依次像上级作用域查找,知道访问到window
对象终止,这一层层关系就是作用域链
作用域链的作用是
保证对执行环境有权访问的所有变量和函数的有序访问 ,通过作用域可以访问到外层环境的函数或变量
什么是VO和AO
AO
:Activetion Object
(活动对象)VO
:Variable Object
(变量对象)
VO
对应的是函数创建阶段,JS
解析引擎进行预解析时,所有的变量和函数的声明,统称为Variable Object
。该变量与执行上下文相关,知道自己的数据存储在哪里,并且知道如何访问。VO
是一个与执行上下文相关的特殊对象,它存储着在上下文中声明的以下内容:
- 变量 (
var
, 变量声明); - 函数声明
(FunctionDeclaration, 缩写为FD);
- 函数的形参
AO
对应的是函数执行阶段,当函数被调用执行时,会建立一个执行上下文,该执行上下文包含了函数所需的所有变量,该变量共同组成了一个新的对象就是Activetion Object
。该对象包含了:
- 函数的所有局部变量
- 函数的所有命名参数
- 函数的参数集合
- 函数的
this
指向
# 对执行上下文的理解特别重要
全局上下文
- 任何不在函数内部的都是全局执行上下文 会首先创建一个全局的
window
对象,并设置this
等于全局对象
- 任何不在函数内部的都是全局执行上下文 会首先创建一个全局的
函数执行上下文
- 当一个函数被调用 会为这个函数创建一个新的执行上下文
eval
函数上下文- 执行
eval
函数代码会有属于他的执行上下文
- 执行
执行上下文栈
- 用来管理执行上下文 程序执行时,遇到全局代码,会创建一个全局执行上下文并压入执行栈中,每遇到一个函数调用 会为该函数创建一个新的执行上下文并压入栈顶,当执行完执行上下文从栈顶弹出,继续下一个函数,最后执行完会从栈中弹出全局上下文
# 并行和并发的区别重要
- 并发是宏观概念,我分别有任务
A
和任务B
,在一段时间内通过任务间的切换完成了这两个任务,称之为并发。 - 并行是微观概念,假设
CPU
中存在两个核心,那么我就可以同时完成任务A
、B
。同时完成多个任务的情况就可以称之为并行。
# 浏览器的垃圾回收机制特别重要
概念
js
代码运行时 需要分配内存来存储变量和值 当变量不参与运行时就需要系统回收被占用的内存空间
垃圾回收的方式:
标记清除 当变量进入执行环境的时候,会被标记"进入环境",内存不能被回收,因为正在被使用,当变量离开环境的时候,会被标记离开环境,系统会释放该内存
- 垃圾回收器的作用是给所有变量都加上标记,然后去除那些被使用的变量的标记,在此之后被加上标记的都是准备删除的变量
引用计数 简单的来说但变量的引用次数为
0
的时候就会被销毁,内存被释放但是当有循环引用的时候就不会被释放 需要手动设置释放内存
xxx = null
减少垃圾回收
当代码很复杂的时候,垃圾回收带来的代价比较大,我们应该尽量减少垃圾回收
对数组进行优化:最简单的方法是赋值为
[]
,但是同时会创建一个新的空对象,所以我们可以把数组的长度设为0
对
Object
进行优化:对象尽量复用,对不使用的对象,将其设置为null
对函数的优化:在循环的函数表达式中,如果可以复用,就尽量放在函数外面
哪些会导致内存泄露
- 意外的全局变量:使用未声明的变量会变成全局变量
- 被遗忘的计数器
- 闭包
- 脱离
DOM
的引用:获取DOM
元素的引用,后面这个元素被删除,由于一直保留这个元素的引用,无法被回收 - 循环引用
- 控制台日志(
console.log
)