【JS】作用域和闭包

99 阅读3分钟

1.作用域

JavaScript是一门编译语言。(动态、解释执行),不是提前编译的(编译过程不是发生在构建之前),执行前编译,编译结果也不能在分布式系统中进行移植。

程序中的一段源代码在执行前会经历三个步骤,统称为编译:

  • 分词/词法分析(区别在进行的方式有状态还是无状态)

    将字符组成的字符串分解成有意义的代码块(词法单元)

  • 解析/语法分析

    词法单元流(数组)=>抽象结构树AST(代表程序语法结构)

  • 代码生成

    将AST转换为可执行代码的过程。

2. var a =2;

  • 引擎

    编译及执行过程

  • 编译器

    语法分析及代码生成

  • 作用域

    确定当前执行的代码对声明的标识符的访问权限

var a =2;

  1. 编译器在编译时处理

    在当前作用域中声明一个变量,如果之前没有声明过

    分解成词法单元-->解析成树结构--->代码生成

  2. 引擎在运行时处理

    引擎在作用域中查找该变量,如果能找到就对它赋值

    一个赋值操作的左侧和右侧。

    编译器可以在代码生成的同时处理声明和值的定义。

    • LHS查询

      变量出现在赋值操作的左侧,试图找到变量的容器本身,从而可以对其赋值,赋值操作的目标。

      LHS查询不到,非严格模式下,在全局作用域中创建该变量,并将其返还给引擎。严格模式禁止自动或隐式地创建全局变量,查询不到,抛类似ReferenceError异常。

    • RHS查询

      (非左侧,取得它的原值)变量出现在赋值操作的右侧,查找某个变量的值,赋值操作的源头。

      RHS查询不到,引擎抛出ReferenceError异常。

      异常:

      • ReferenceError同作用域有关
      • TypeError作用域判别成功,对结果的操作非法或不合理。

3.作用域嵌套

遍历嵌套作用域:从当前作用域(执行作用域)开始向上级作用域查找。作用域查找会在找到第一个匹配的标识符时停止。

“遮蔽效应”,内部的标识符遮蔽了外部的标识符。

全局变量被遮蔽,可通过引用全局对象的属性访问window.a

4.词法作用域

  1. 词法阶段

    ​ 词法作用域:定义在词法阶段的作用域。

    函数的词法作用域都只由函数被声明时所处的位置决定。

    ​ 大部分情况下,词法分析器处理代码时会保持作用域不变。

    • 词法作用域查找

      只会查找一级字符

    • 对象属性访问规则

  2. 欺骗词法

    欺骗词法作用域会导致性能下降。引擎无法在无法在编译时对作用域进行查找进行优化。

    • eval(...)

      参数:字符串

      将传入的内容视为好像在书写时(词法期)就存在于程序中这个位置的代码,修改词法作用域的环境。

      通常被用来执行动态创建的代码,在运行期修改书写期的词法作用域。严格模式下,无法修改所在的作用域。

    • with

      重复引用同一个对象中的多个属性的快捷方式。

      正常的var声明不会被限制在块的作用域。

      严格模式禁止。