# 一.let

前言

  • var 定义的变量没有块的概念,可以跨块访问,不能跨函数访问,有变量提升,可重复声明
  • let 定义的变量,只能在块作用域里访问,不能跨块访问,也不能跨函数访问,无变量提升,不可以重复声明
  • let 声明的变量只在块级作用域内有效,不存在变量提升,不可以重复声明
  • 或者说 let 变量提升了,但是在 let 声明变量前不能使用该变量,这特性叫暂时性死区
  • 如果有重复变量let会在编译阶段报错

# 1.声明

# 1.1 重复声明

  • 在同一个作用域下可以多次声明同一个变量(let 不能重复声明)
var a = 1
function b() {
  var a = 2
  var a = 4 // 正常运行
}
b()
1
2
3
4
5
6

报错

var a = 1
function b() {
  let a = 2
  let a = 4 // 提示 a 已经被声明过了
}
b()
1
2
3
4
5
6

在不同的作用域声明同一个变量是可以的

let a = 1
function b() {
  let a = 2
}
b()
1
2
3
4
5

如果用 let 声明过了 就不要再 用 var 了

function b() {
  let a = 2
  var a = 4 //报错
}
b()
1
2
3
4
5

# 2.作用域

# 2.1 块级作用域

  • 允许块级作用域任意嵌套
  • 外层作用域无法读取内层作用域的变量
  • 内层作用域可以定义外层作用域的同名变量
  • 函数本身的作用域在其所在的块级作用域之内
"use strict"
function fn() {
  console.log("out")
}
;(function () {
  if (false) {
    function fn() {
      console.log("in")
    }
  }
  fn()
})()
1
2
3
4
5
6
7
8
9
10
11
12

在用 var 定义变量的时候,变量是通过闭包进行隔离的,现在有了 let,不仅仅可以通过闭包进行隔离,还增加了一些块级作用域隔离。块及作用域用一组大括号定义一个块,使用 let 定义的变量在大括号的外面是访问不到的

for (let i = 0; i < 3; i++) {
  setTimeout(function () {
    console.log(i)
  }, 1000)
}
console.log(i)
// Uncaught ReferenceError: i is not defined
//     at <anonymous>:7:13
// (anonymous) @ VM1874:7
// 0
// 1
// 2
1
2
3
4
5
6
7
8
9
10
11
12

先执行同步代码,i 报错,然后执行异步代码,由于是块级作用域,let i=0 会保存在{}中

let 支持块级作用域声明的变量只会声明在当前的作用域内

let 可以解决作用域污染的问题 和局部作用域的问题

# 2.2 变量提升

console.log(a) // 块级作用域,预解释阶段变量不会提升,此处没有找到变量a会报错
let a = 1
1
2
  • 2.暂存死区

如果作用域内有这样一个变量,这个作用域内就会绑定这个变量,不会继续向上查找

let a = 1
{
  console.log(a) //  a is not defined
  let a = 2
}
1
2
3
4
5

console.log(a)首先会在{}这个作用域中查找 a 变量,因为 a 在这个作用域中已经声明过,但是 let 没有变量提升,所有会报错

同样

let a = 1
{
  console.log(a) // 1
  a = 2
}
1
2
3
4
5

# 3.实现

实现块及作用域

if (true) {
  let name = "abc"
}
console.log(name) // ReferenceError: name is not defined
1
2
3
4

不会污染全局对象

if (true) {
  let name = "abc"
}
console.log(window.name) // undefined
1
2
3
4

for 循环中也可以使用 i

for (let i = 0; i < 3; i++) {
  console.log("out", i)
  for (let i = 0; i < 2; i++) {
    console.log("in", i)
  }
}
// out 0 in 0 in 1 out 1 in 0 in 1 out 2 in 0 in 1
1
2
3
4
5
6
7

重复定义报错

if (true) {
  let a = 1
  let a = 2 //Identifier 'a' has already been declared
}
1
2
3
4

不存在变量的预解释

for (let i = 0; i < 2; i++) {
  console.log("inner", i) // i is not defined
  let i = 100
}
1
2
3
4

闭包的新写法

// 以前
;(function () {})()
// 现在
{
}
1
2
3
4
5