作用域(Scope)、执行上下文(Context)、闭包(Closure)
1. 作用域
1.什么是作用域
1.作用域定义
负责收集并维护由所有标识符组成的一系列查询,并实施一套严格的规则,确定当前执行的代码对这些标识符的访问权限。
2.作用域嵌套
当一个块或函数嵌套在另一个块或函数就发生了作用域嵌套。因此在当前的作用域中无法找到某个变量时,引擎就会在外层嵌套的作用域中继续查找,直到找到该变量,或抵达罪外层的作用域(也就是全局作用域)为止。
2.词法作用域
1.作用域的两种工作模式
作用域共有两种主要的工作模式。第一种最为普遍,叫做词法作用域,是指由你在写代码时将变量和块作用域写在哪里决定的,因此当词法分析器处理代码时会保持作用域不变。无论函数在哪里被调用,也无论它如何被调用,他的词法作用域只由函数声明时所处的位置决定,Javascript的作用域是词法作用域。另一种为动态作用域,是指在函数在运行时确定的作用域,this的机制某种程度上像动态作用域。
词法作用域关注函数在何处声明,而动态作用域关注函数从何处调用。
2.作用域的两个部分
1.Environment Record
记录作用域内变量信息(我们假设变量,常量,函数等统称为变量)和代码结构信息的东西,称之为 Environment Record
2.outer
一个引用 outer,这个引用指向当前作用域的父作用域。拿上面代码为例。全局作用域的 outer 为 null。
3.欺骗词法
eval(…)和with 会在运行时修改或创建新的作用域,以此来欺骗其他在书写时定义的词法作用域。
Javascript引擎在编译阶段会进行性能优化。其中有些优化依赖于能够根据代码词法进行静态分析,并预先确定所有变量和函数的定义位置,才能在执行过程中快速找到标识符。但如果引擎在代码中发现了eval(…)和with时,就简单假设关于标识符的位置的判断都是无效的,最悲观的情况是所有的优化都是无效的,因此最简单的做法就是完全不做任何优化。
3.函数作用域和块作用域
1.函数中的作用域
1.定义
函数作用域是指这个函数的全部变量都可以在整个函数的范围内使用及复用,能充分利用Javascript可以需求改变值类型的动态特性。
2.函数作用域的优点
函数作用域实际是在这个代码片段的周围创建一个作用域气泡,可以将内部的变量和函数定义“隐藏起来”,外部作用域无法访问包装函数内部的任何内容。符合最小授权或最小暴露原则,同时可以避免同名标识符之间的冲突,冲突会导致变量的值被意外覆盖。
2.块作用域
任何一对花括号{}中的语句都属于一个块,在这之中定义的所有变量在代码块外是不可见的,我们称之为块级作用域。可以用try/catch, with,let和const创建作用域。
2.执行上下文
1.什么是执行上下文
1.定义
执行上下文是在函数被调用时,但在函数真正被执行时创建的。函数被调用时,引擎会检查函数中的参数、申明的变量以及内部的函数,然后基于这些信息建立执行上下文对象。在这个阶段变量对象,作用域链和this的指向都会被确定,也可以将执行上下文看为代码块的运行环境。
2.代码运行的三种环境
- 全局级的代码-这个是默认的代码运行环境,一旦代码被载入,引擎最先进入的就是这个环境。
- 函数级的代码-当执行一个函数,运行函数体内的代码。
- Eval级的代码-在eval函数内运行的代码
3.执行上下文的特征
- 一段代码块对应一个执行上下文,被封装成一个函数的代码视为一段代码块,或者“全局作用域”也被视作一段代码块。
- 当程序运行进入一段代码块时,新的执行上下文被创建,并被推入执行栈中。当程序运行到这段程序结尾后,对应的执行上下文被弹出执行栈。
- 当程序运行到某个点需要转到另一个代码块中,那么当前的执行上下文环境会被挂起,然后执行一个新的执行上下文放到执行栈顶部。
- 执行栈 最顶部的可执行上下文被称为 running execution context。当顶部的可执行上下文被弹出后,上一个挂起的可执行上下文继续执行. stack 最顶部的可执行上下文被称为 running execution context。当顶部的可执行上下文被弹出后,上一个挂起的可执行上下文继续执行.
4.执行上下文的内部属性
- [[code evaluation]] :当前代码执行的状态,perform、resume、suspend。
- [[Function]]:若当前执行执行上下文对应的是一个函数,则这个属性就保存的这个函数对象。如果对应的是一个全局变量(可以是Script、module),那么是null。
2.执行上下文建立和执行的分析
- 找到当前上下文调用的函数代码。
- 在执行被调用的函数体重的代码之前,创建对应的函数上下文。
- 第一阶段----执行上下文创建阶段
- 建立变量对象:
- 建立参数对象,检查当前上下文中的参数,建立该对象下的属性以及属性值。
- 检查当前上下文中的函数声明:
- 每找到一个函数声明,就在变量下面用函数名建立一个属性,属性值就是指向该函数在内存中的地址的一个引用
- 如果上述函数名已存在于变量对象中,那么对应的属性值会被新的对象引用覆盖
- 检查当前上下文中的变量声明:
- 每找到一个声明,就在变量对象中用变量名建立一个属性,属性值为undefined
- 如果该变量名已经存在于变量对象的属性中,直接跳过(防止指向函数的属性的值被变量属性覆盖为undefined),原属性值不会被修改。
- 初始化作用域链
- 确定上下文中this的指向对象。
- 建立变量对象:
- 第二阶段 ---- 代码执行阶段
- 执行代码体中的代码一行一行的执行代码,给变量对象的变量属性赋值。
3.闭包
1.闭包的定义
一个拥有多个变量和绑定这些变量的环境表达式(通常是一个函数),因而这些变量也是这些表达式的一部分。
- 作为一个函数变量的引用,当函数返回时,他的环境变量为激活状态(也就是它的执行上下文*[[code evaluation]]*处于perform状态)。
- 一个闭包就是函数返回时没有释放资源的栈。
2.闭包的产生的条件
- 词法作用域
- 函数可以作为参数进行传递。
3.闭包的应用
- 模仿块级作用域
- 存储私有变量
- 封装私有变量