let
提示
从上一节可以知道,使用var声明变量的方式弊端很多,因此ES6增加了两种新的声明方式:let和const。在这一节中,我们先来介绍一下let。
let简介
在ES6中,我们可以使用let来声明一个变量。虽然let和var都可以用来定义变量,但是let有以下4个非常重要的特点。
1.只在代码块中有效
使用let 声明的变量,只在代码块之内有效,即只在块级作用域之内有效
示例1
if (true) {
let title = '海贼王'
}
console.log(title)
>>> Uncaught ReferenceError: title is not defined
分析
使用let声明的变量只在其所在代码块之内有效,在代码块之外访问会报错。
示例2
let title = '火影忍者'
if (true) {
let title = '海贼王'
console.log(title)
}
console.log(title)
>>> 海贼王
>>> 火影忍者
分析
在这个例子中,如果两个变量都使用var来声明,则最终两个console.log0都会输出“海贼王”。从这个例子中也可以看出,使用let来代巷var,可以避免这种内层变量覆盖外层变量的不合理情况。
2.同一代码块中,不允许重复声明
使用let声明的变量,在其所在的代码块中是不允许重复声明的。
示例
if (true) {
let title = '火影忍者'
let title = '海贼王'
console.log(title)
}
>>> Uncaught SyntaxError: Identifier 'title' has already been declared
分析
虽然在同一代码块中,变量不允许被let重复声明,但是这个变量却可以被重新赋值。要注意,声明与赋值是两码事。请看下面的代码。
if (true) {
let title = '火影忍者'
title = '海贼王'
console.log(title) // 海贼王
}
3.不存在变量提升
用let声明变量不像用var声明变量那样会出现变量提升的现象。因此,用let声明的变量只能在声明后使用,如果在声明之前使用,就会报错。
示例: 变量
console.log(title);
let title = '火影忍者';
>>> Uncaught ReferenceError: Cannot access 'title' before initialization
示例: 函数
console.log(fn)
let fn = function () {
console.log('火影忍者')
}
>>> Uncaught ReferenceError: Cannot access 'fn' before initialization
4.不会成为window的属性
在ES6之前,在全局作用域中,使用var声明的变量会成为window对象的属性。但是在ES6的全局作用域中,使用let声明的变量不会成为window对象的属性。
示例:ES5中的var
var a = 2077
var fn = function () {
console.log(2077)
}
console.log(window.a)
window.fn()
>>> 2077
>>> 2077
示例: ES6中的let
let a = 2077
let fn = function () {
console.log(2077)
}
console.log(window.a)
window.fn()
>>> Uncaught TypeError: window.fn is not a function
let的用途
使用var来定义变量可能引发的一个不合理情况,就是用来计数的循环变量泄露为全局变量.我们先来看一个例子。
示例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script>
window.onload = function () {
var oBtn = document.getElementsByTagName('input')
for (var i = 0; i < oBtn.length; i++) {
oBtn[i].onclick = function () {
console.log(i + 1)
}
}
}
</script>
</head>
<body>
<input type="button" value="按钮1" />
<input type="button" value="按钮2" />
<input type="button" value="按钮3" />
<input type="button" value="按钮4" />
<input type="button" value="按钮5" />
</body>
</html>

分析
闭包这种方式比较生涩难懂,也不直观。在ES6中,我们可以使用let巧妙地解决这个问题。
示例: 使用let
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script>
window.onload = function () {
var oBtn = document.getElementsByTagName('input')
for (let i = 0; i < oBtn.length; i++) {
oBtn[i].onclick = function () {
console.log(i + 1)
}
}
}
</script>
</head>
<body>
<input type="button" value="按钮1" />
<input type="button" value="按钮2" />
<input type="button" value="按钮3" />
<input type="button" value="按钮4" />
<input type="button" value="按钮5" />
</body>
</html>

分析
由于let声明的变量拥有块级作用域,用来计数的for循环变量(也就是i)不会泄露为全局变量,从而能避免被覆盖。这是一个非常常见而重要的场景,小伙伴们要重点掌握。
