• 编译原理
    js中的源代码在执行之前会经历三个步骤,统称为编译:

    1. 分词/词法分析
      这个过程会将由字符组成的字符串分解成有意义的代码块,这些代码块被称为词法单元(token)

      1
      2
      // 例如
      var a = 1;

      以上的代码会被分解为:var,a,=,2,;如果在某些语言中的空格是有意义的,那么空格也会被编译.

    2. 解析/语法分析
      这个过程是将词法单元流(数组)转换成一个由元素逐级嵌套所组成的代表了程序语法结构的书。这个树被称为”抽象语法树”(AST)。如上面的例子中,在抽象语法树中可能会有一个叫做VariableDeclaration的顶级节点,该节点下面是一个叫做Identifier的子节点(a),和一个叫做AssignmentExpression的子节点。AssignmentExpression节点有一个叫做NumericLiteral(2)的子节点。
    3. 代码生成
      经过第二步的解析/语法分析之后,AST转换为可执行代码(机器指令)的过程被称为代码生成。该过程和语言、平台都存在关系。
  • 作用域
    在理解作用域之前,先了解以下两个概念
    引擎:从头到尾负责整个js程序的编译及执行过程
    编译器:负责语法分析及代码生成等
    在了解了上面的两个概念之后,我们来看作用域:
    作用域:负责收集并维护由所有声明的标识符(变量)组成的一系列查询,并实施一套非常严格的规则,确定当前执行的代码对这些标识符的访问呢权限。也就是说作用域是根据名称查找变量的一套规则。

  • 作用域嵌套
    当一个块或函数嵌套在另一个块或者函数中时,就会发生作用域嵌套,当在当前的作用域中无法找到某个变量时,引擎就会在外层嵌套的作用域中继续查找,直到找到该变量,或者抵达最外层的作用域为止(全局作用域)

  • 词法作用域
    词法作用域就是定义在词法阶段的作用域,也就是说,词法作用域是由你写代码时将变量和块作用域写在哪里来决定的,因此当词法分析器处理代码时会保持作用域不变。

    1
    2
    3
    4
    5
    6
    7
    8
    function foo(a){ // (2)
    var b = a * 2
    function bar(c){
    console.log(a,b,c) //(3)
    }
    bar(b * 3)
    }
    foo (2); // 2,4,12; (1)

    在上面的例子中出现了三个嵌套的作用域(1),(2),(3)

  • 变量声明提升
    js引擎会在解释js代码之前首先对其进行编译,而编译阶段中的一部分工作就是找到所有的声明,并用合适的作用域将他们关联起来,因此,包括变量函数在内的所有声明都会在任何代码被执行前首先被处理.

    1
    var a = 2;

    所以当在js中写入上面的代码时,会经历两个步骤:

    1. 定义声明 var a;是在编译阶段完成
    2. a = 2;赋值会在原地等待执行阶段完成
      也就是说首先会进行编译,然后才是执行.
  • 函数优先
    函数声明和变量声明都会被提升。但是函数首先会被提升,然后才是变量.函数声明会被提升到普通变量之前。