# let, const, var

# 变量提升

当你使用var去定义变量时, 在编译 JS 时变量会全部提升到当前作用域的顶部.

同时在全局作用域定义的变量都会成为window的属性

# 作用域

# ES6 之前

只有全局作用域函数作用域

# ES6 之后

全局作用域函数作用域块级作用域

# 创建块级作用域

ES6 后新增了块级作用域, 用{}括住:

{
  let x = 1;
}
console.log(x); // Uncaught ReferenceError: x is not defined
1
2
3
4

块级作用域内的变量不能被外部作用域调用, 而let/const的特点之一就是声明之后便创建一个块级作用域.

#for循环看letvar的区别

使用var:

for (var i = 0; i < 5; i++) {
  setTimeout(() => {
    console.log(i);
  }, 1000);
}

5;
5;
5;
5;
5;
1
2
3
4
5
6
7
8
9
10
11

使用let:

for (let i = 0; i < 5; i++) {
  setTimeout(() => {
    console.log(i);
  }, 1000);
}

0;
1;
2;
3;
4;
1
2
3
4
5
6
7
8
9
10
11

# Analysis

当用var声明i时, 进行异步循环打出了 5 个5, 实际上是在异步开始后执行()里的代码:

var i = 0;
// i < 5 满足
i = 1;
i = 2;
i = 3;
i = 4;
i = 5;
// i < 5 不满足
console.log(i);
console.log(i);
console.log(i);
console.log(i);
console.log(i);
1
2
3
4
5
6
7
8
9
10
11
12
13

当用let声明i时, 情况也将大不相同, 从别人的文章里发现.let有两个很有特点的地方:

  1. for( let i = 0; i< 5; i++) 这句话的圆括号之间,有一个隐藏的作用域
  2. for( let i = 0; i< 5; i++) { 循环体 } 在每次执行循环体之前,JS 引擎会把 i 在循环体的上下文中重新声明及初始化一次。

用代码来表述就是:

{
  let i = 0;
  console.log(i);
}
{
  let i = 1; // for循环会记住上次循环结果并赋值给下个循环
  console.log(i);
}
{
  let i = 2;
  console.log(i);
}
{
  let i = 3;
  console.log(i);
}
{
  let i = 4;
  console.log(i);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# Temporary Dead Zone

{
  console.log(x);
  let x = 1;
}
// Uncaught ReferenceError: Cannot access 'x' before initialization
1
2
3
4
5

报错的意思是不能在初始化之前访问, let/const的特点就是在创建(created)前的任何操作(访问, 初始化, 赋值)都是不被允许的.而在被创建的之前的部分叫做暂时性死区(Temporary Dead Zone, TDZ).

探究 TDZ 的原理时, 我们也发现了let/const也会提升的事实, 但和var的提升大不相同:

  1. var在创建(created)时就被初始化(initialize)为undefined

  2. let/const在进入块级作用域后,会因为提升的原因先创建,但不会被初始化,直到声明语句执行的时候才被初始化,初始化的时候如果使用let声明的变量没有赋值,则会默认赋值为undefined,而const必须在初始化的时候赋值。而创建到初始化之间的代码片段就形成了暂时性死区

    let u;
    console.log(u);
    // undefined
    
    1
    2
    3
    const c
    // Missing initializer in const declaration
    
    1
    2

# 函数提升

函数声明会提升, 函数表达式不会

/* Function declaration */

foo(); // "bar"

function foo() {
  console.log("bar");
}

/* Function expression */

baz(); // TypeError: baz is not a function

var baz = function() {
  console.log("bar2");
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15