精读《你不知道的JavaScript》上卷-I-第1章作用域是什么

159 阅读3分钟

第1章作用域是什么

编译原理

JavaScript是一门编译语言

传统的编译语言在代码执行前会经历三个步骤(编译)

  • 分词/词法分析(Tokenizing/Lexing)

    将由字符组成的字符串分解成(对编程语言来说)有意义的代码块(词法单元token)

  • 解析/语法分析(Parsing)

    将词法单元流(数组)转换成一个由元素逐级嵌套所组成的代表了程序语法结构的树(抽象语法树Abstract Syntax Tree,AST)。

  • 代码生成

    将 AST 转换为可执行代码

JavaScript引擎编译过程比普通编译语言更复杂 在语法分析和代码生成阶段有特定的步骤来对运行性能进行优化,包括对冗余元素进行优化等。 用尽了各种办法(比如 JIT,可以延迟编译甚至实施重编译)来保证性能最佳。

理解作用域(建议多读几遍)

当变量出现在赋值操作的左侧时进行 LHS 查询,出现在右侧时进行 RHS 查询。

可以将 RHS 理解成 retrieve his source value(取到它的源值),这意味着“得到某某的值”。 在概念上最好将其理解为“赋值操作的目标是谁(LHS)”以及“谁是赋值操作的源头(RHS)”。

如果查找的目的是对变量进行赋值,那么就会使用 LHS 查询;如果目的是获取变量的值,就会使用 RHS 查询。

赋值操作符会导致 LHS 查询。=操作符或调用函数时传入参数的操作都会导致关联作用域的赋值操作。

作用域

作用域是一套规则,用于确定在何处以及如何查找变量(标识符)。

遍历嵌套作用域链的规则: 从当前的执行作用域开始查找变量,如果找不到,就向上一级继续查找。当抵达最外层的全局作用域时,无论找到还是没找到,查找过程都会停止。


编码经验:减少无意义的变量查找次数和未定义变量的查找,可以提升效率


异常

RHS 查询遍寻不到所需的变量,引擎就会抛出 ReferenceError 异常。

非严格模式:引擎执行 LHS 查询时,如果在顶层(全局作用域)中也无法找到目标变量, 全局作用域中就会创建一个具有该名称的变量,并将其返还给引擎。

严格模式中 LHS 查询失败时,并不会创建并返回一个全局变量,引擎会抛出同 RHS 查询 失败时类似的 ReferenceError 异常。

如果 RHS 查询找到了一个变量,但是你尝试对这个变量的值进行不合理的操作, 比如试图对一个非函数类型的值进行函数调用,或者引用 null 或 undefined 类型的值中的属性,那么引擎会抛出另外一种类型的异常,叫作 TypeError。

ReferenceError 同作用域判别失败相关,而 TypeError 则代表作用域判别成功了,但是对结果的操作是非法或不合理的。

案例

function foo(a) {
    var b = a;
    return a + b;
}
var c = foo( 2 );
  1. 找出所有的LHS查询(这里有3处!)

    c = ..;、a = 2(隐式变量分配)、b = ..

  2. 找出所有的RHS查询(这里有4处!)

    foo(2..、= a;、a ..、.. b