1. 90前端首页
  2. 前端开发
  3. JavaScript

JavaScript 预解析

JavaScript 预解析

在了解预解析之前我们先了解下 词法作用域 的概念

词法作用域

1. 什么是作用域

作用域就是指一个变量可以被使用的范围. 即从什么地方开始可以被访问到. 到什么地方结束, 不能在访问到, 这个范围称为变量的作用域.

2. 什么是块级作用域

块级作用域就是使用代码块来限定变量的使用范围.在 js 中( ES5 以前 )没有块级作用域.在 es6 中引入了 let 命令, 来代替 var 声明变量. 而 let 命令声明的变量就具有块级作用域.

所谓的块级作用域就是从变量声明开始, 到变量所在的最近的(最小的)花括号结束为止.

3. js 中的词法作用域

在 js 中 js 的代码需要经过\"预解析\"( 提前解析 ), 再逐步的解释执行.
所以在 js 中所谓的词法作用域从预解析开始全局起作用, 只有函数可以限制作用域的范围.

代码预解析

1. js 中的声明

声明就是 变量的声明和函数的声明, 其目的是让 js 解释引擎知道有什么东西.
声明时不参与运算的, 是不参与执行的, 是在预解析阶段就完成的.

  • 变量的声明
    // 变量的声明就是 var 变量名.
    var num = 123;
    // 这是一个语法糖,可以理解成
    var num;
    num = 123;
  • 函数的声明
    function 函数名 () { ... }
    //  在一个独立于任何语句( 表达式, if 结构, while 结构 等 )的独立结构中, 或函数中出现的代码, 为函数声明.

2. js 预解析代码如何执行

js 的代码执行要经理两个步骤, 首先是预解析. 预解析会通读所有代码. 如果发现错误则停下, 如果遇到声明则记录.

在声明的时候, 如果是变量名的声明, 解析器内部就会记录下这个变量. 如果使用遍历则检查是否有记录.

在声明的时候, 如果是函数声明, 则 解析器会先记录函数的名字( 相当于变量声明 ), 然后将函数的名字与函数体联系在一起.

在预解析中, 如果出现重复声明, 则第一次声明起作用, 其后所有的同名的声明无效.

// 例如:
  var num = 1;
  var num = 2;
// 等价
  var num = 1;
  num = 2;

声明结束后代码就会再从第一句话开始一句一句的执行.

3. 示例

  • 例子 1
    if ( \'a\' in window ) {
      var a = \'hello js\';
    }
    console.log( a );

    解析:

    1: 代码需要预解析, 通篇阅读代码, 发现有声明 var a = \'Hello js\' 这个语句. 则 js 引擎记录下变量 a
    2: 除此之外没有其他声明预解析结束, 代码等价于

    if ( \'a\' in window ) {
      a = \'hello js\';
    }
    console.log( a );

    3: 开始执行代码:

    1. 首先判断 \'a\' in window
    2. 由于预解析时记录了 变量名 a, 表示 a 已经存在. 因此表达式为 true
    3. 进入 if 结构中, 执行赋值语句, a 存储字符串 \'Hello js\'
    4. 出 if 结构, 打印 a 的值, 结果为 \'Hello js\'
  • 例子 2
     console.log( func );
     var func = 123;
     function func() {
     console.log( \'Hello js\' );
     }

    解析:

    1: 预解析, 发现声明 func 这个变量, js 引擎记录名字 func

    2: 发现声明 function func () ..., js 首先检查名字是否已经记录, 发现已经存在名字 func, 重复声明不再记录名字

    3: 由于声明时函数, 将函数体与已存在的名字 func 相关连, 预解析结束

    4: 代码则等价于:

     console.log( func );
     func = 123;

    5: 执行第一句话, 打印 func, 在 js 引擎中已经将 func 与函数关联, 因此打印函数体
    6: 执行赋值 func 结果为 123

在访问某一个变量的时候, 首先会在当前函数中查找有没有该变量声明,如果有则只在当前函数中访问, 如果没有声明则访问函数外的变量

  • 例子 3
    f2();
    var num = 123;
    function f1 () {
      console.log( num );
    }
    function f2 () {
      var num = 456;
      f1();
    }
    console.log( num );

    解析:

    1: 预解析, 记录下 num, f1, 和 f2, 同时代码 f1 与 f2 与对应的函数体相关联.

    2: 预解析结束, 代码就等价于

    f2();
    num = 123;
    console.log( num );

    3: 开始执行第一句话, 调用 f2. 凡是进入函数体, 又会进行一次预解析

    4: 进入函数 f2, 发现变量 num, 记录下 num. 函数内的预解析结束, 代码变成

    num = 456;
    f1();

    5: 开始执行函数 f2 内的代码, 首先给 num 赋值, num 的取值为 456

    6: 调用 f1(), 进行检查, 当前函数中是否有 f1 的声明? 没有在 f2 中找到 f1 的声明, 因此在外面去找.

    7: 外面有函数的声明, 则调用的是外面的函数, 进入函数 f1, 进行预解析. f1 中没有任何 声明语句则直接开始执行.

    8: 执行代码打印 num. 检查 num 是否在函数内部有声明? 因此到外面去找. 到全局范围去查找, js 引擎中已经记录了名字 num

    9: 由于还没没有执行赋值语句, 打印的结果为 undefined. 函数 f1 执行结束, 回到 f2 中

    10: f2 执行结束, 开始执行 赋值语句 num = 123, 执行完成执行下一句打印, 结果为 123

本文来自网络整理,转载请注明原出处:https://segmentfault.com/a/1190000021643107

展开阅读全文

发表评论

登录后才能评论