提升

这里我们不考虑 ES6 这部分的知识点

前期知识准备

1、首先先理解 JavaScript 是如何执行代码的

JavaScript 是动态语言,在执行代码时要先进行编译。

  • 第一部分是编译代码(主要是全局声明函数和变量),并且生成代码(将用户的代码生成符合 JavaScript 正确顺序执行,具体顺序后面有细讲)

  • 第二部分是执行代码(执行函数或变量赋值)

2、了解 JavaScript 中的 引擎、编译器、作用域三者的作用及区别

  • 引擎:从头到尾负责整个 JavaScript 程序的编译及执行过程。
  • 编译器:负责语法分析及代码生成
  • 作用域:负责收集并维护有所有声明的标识符(变量)组成的一系列查询,并实施非常严格的规则,确定当前执行的代码对这些标识符的访问权限。

上才艺

1
2
3
4
5
6
7
8
9
10
11
12
13
// 调用函数
fun1();

// 声明全局变量
var g = 2;

// 函数声明
function fun1() {
console.log(b);
var b = 0;
console.log(b);
console.log(g);
}
  • 补充:

    • 局部作用域 指的是函数内部(函数分很多种,这里是所有函数)

    • var b =2 在程序中是被拆分成 var b b =2

    • 函数声明的优先级要高于变量

      1
      2
      3
      var a = 10;
      function a() {}
      console.log(typeof a) // number
      • 函数可以分为函数声明,函数表达式,立即执行函数等,但唯独函数声明会提升
  • 第一步:程序会被转成 JavaScript 内部认为正确的顺序

    • 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      function fun1() {
      var b;
      console.log(b); // undefined
      b = 0;
      console.log(b); // 0
      console.log(g); // undefined
      }
      var g;
      fun1();
      g = 2;
    • 依据以下几点来判断其顺序
      • 从上往下(全局)看:看到变量声明或函数表达式时将其提到最前面。且函数声明的优先级要高于变量(可以理解为在当前作用域中函数表达式始终是在最前面的)
      • 接下来就是调整局部作用域中的函数表达式和变量声明,依据跟上面一样。
  • 第二步:将全局及局部变量和函数告诉作用域,作用域会根据信息给变量和函数创建相对应的空间

    • 如何创建依据以下几点

      • 前提:变量和函数在全局下。直接在顶级作用域声明变量和函数
      • 局部(函数)下:在局部作用域下声明变量和函数。
  • 第三步:代码执行阶段,就是按第一步生成的顺序从上到下来执行。

在上面的小例子中,在 JavaScript 执行被我分为三部分。真正执行是俩部分(编译阶段和执行阶段),只不过在编译阶段要经历第一步和第二步。为了方便理解,我把它细分出来。

执行阶段

具体看下面代码

1
2
3
4
5
(function() {
var a = b = 5;
})()
console.log(b);
console.log(a);

根据上面的例子,很容易可以得到真正的执行顺序

1
2
3
4
5
6
7
(function() {
var a;
a = b;
b = 5;
})();
console.log(b); // 5
console.log(a); // Uncaughte ReferenceError: a is not defined

这里跟第一个案例的区别在于执行阶段

在 a = b 赋值阶段,首先

  • 引擎会问作用域在立即执行函数中是否存在 变量 b ;
  • 作用域在该作用域(立即执行函数)下找变量 b,若没有找到。此时作用域会接着往上一层作用域找变量 b 直到找到顶级作用域还没有找到,直接告诉引擎编译器没有声明该变量。
  • 引擎接收到信息后,执行 a = b 由于程序中并没有存在变量 b , 故执行 console.log(a); 会报错 Uncaughte ReferenceError: a is not defined

在 b = 5 这个阶段跟上面是不一样,在赋值前变量已经被创建了,只不过是在全局下声明,故在 console.log(b) 输出结果为 5

这里就涉及到了 RHS 和 LHS