JavaScript-变量提升与函数提升

前言

什么是变量提升?
变量提升是对JavaScript(hoisting)执行上下文的一种认识。不管变量是在作用域的那个地方声明的,都会提升到当前作用域的最顶部。
JavaScript 中,变量可以在使用后声明,也就是变量可以先使用再声明。

预解析

预解析 -> 变量提升、函数提升(声明变量和函数) -> 执行代码 、执行赋值
变量提升和函数提升不过是预解析的执行过程。

1
// 解析之前
2
console.log(a);
3
function a() {
4
  console.log('aaaa');
5
}
6
var a = 1;
7
console.log(a);
8
9
//解析之后
10
var a;
11
function a() {
12
  console.log('aaaa');
13
}
14
console.log(a);
15
a = 1;
16
console.log(a);

在解析过程中如果函数和变量的名字相同时,此时函数优先。所以上面的代码块会先输出函数a,在输出1

变量提升

预解析时,把变量的声明提升到当前作用域的最顶部,不包括变量的赋值。

1
// 解析之前
2
console.log(num);
3
var num = 1;
4
5
// 预解析
6
var num;
7
console.log(num);
8
num = 1;

如上面代码所示,解析之前会报 undefined,而不是 num is not defined,就是因为 js引擎预解析将变量
num已经声明,只是没有赋值。

1
var x = 1;
2
3
function f() {
4
  console.log(x,y)
5
}
6
7
var y;
8
y = 2;
9
f();

JavaScript 编译时会先将 var y; 声明提升至顶部。 执行时变量已经声明了y,所以输出 1 2。

函数提升

预解析时,把函数的声明提升到当前作用域的最顶部,不包括函数的调用。
函数提升的前提是函数声明,而不是函数表达式。

1
f();
2
function f() {
3
  console.log('函数声明');
4
}
5
6
test();
7
var test = function(){
8
  console.log('函数表达式');
9
}

上面的例子会输出:

1
函数声明
2
Uncaught TypeError: test is not a function

它们之间存在的差异原因是:
用函数声明创建的函数可以在函数解析后调用(解析时进行等逻辑处理)。
而用函数表达式创建的函数是在运行时进行赋值,且要等到表达式赋值完成后才能调用。

ES6 中没有变量提升

例1

1
function sayHi() {
2
  console.log(name);  // undefined
3
  console.log(age);   // error
4
  var name = 'asd'; 
5
  let age = 22;
6
}
7
8
sayHi();

会输出Cannot access 'age' before initialization,无法在初始化前访问age

例2

1
for (var i =0;i<3;i++){
2
  setTimeout(()=>console.log(i),1);
3
} 
4
5
for (let i = 0;i<3;i++){
6
  setTimeout(()=>console.log(i),1);
7
}

上面代码输出: 333 和 0 1 2的原因是:

  1. Js 是单线程的,setTimeout是异步宏任务,所以代码执行遇到异步的,就放在事件队列中,等线程中的任务执行完后
    才会执行事件队列中的任务。
  2. letES6中声明变量的方式,由自己的作用域块,可以放变量,所以let绑定for循环时,每个i
    都有自己的值,在这个for循环中就是满足一次条件向事件队列中添加一个打印事件,且每个事件都有自己的值。
  3. var没有自己的作用域块,for循环的变量就会后面一个覆盖前一个,当循环完毕时i就取的是最后一次的值.