在 JavaScript 中,是没有块级作用域的概念的,在代码块内声明的变量,其作用域是整个函数作用域而不是块级作用域。let 和 const 作为加强版的 var,让程序员写出更安全,更高效的代码。
let
用 let 定义的变量与 var 有三个区别:块级作用域、不会变量提升,不能定义同名变量。
块级作用域
var 是函数作用域;let 是块作用域。在 ES6 之前,是没有块级作用域的说法的:
1 | while (true) { |
while 体里面定义的变量在代码块外也可以访问到,而使用 let 就不会出现这个问题。
1 | while (true) { |
变量提升
1 | function test() { |
使用 var 定义的变量,JavaScript 解析器会自动把定义搬到最前面,然后在原来定义的地方赋值。所以上述代码就变成了:
1 | var value |
而 let 则不会出现这样的问题
1 | function test () { |
同名变量
用 var 定义变量时,我们可以多次对它进行定义,例如:
1 | var a = 1 |
这样的代码是不会报错的,在 let 定义的相同块中定义同名变量时就会报错了,例如:
1 | let a = 1 |
const
const 除了具有 let 的块级作用域和不会变量提升外,还有就是它定义的是常量,在用 const 定义变量后,我们就不能修改它了。
1 | const AA = 2 |
每一个通过 const 声明的变量必须进行初始化,否则抛出语法错误。
1 | const name //Uncaught SyntaxError: Missing initializer in const declaration |
const 声明的对象不能修改绑定,但是允许修改值,这也就意味着 const 声明的对象可以修改属性值。
1 | const person = { |
临时死区
var 声明的变量会自动提升, let 和 const 声明的变量则不会,如果在声明之前访问这些变量,则会引发错误。从作用域顶部到声明变量语句之前的这个区域,成为临时死区(temporal dead zone) 简称 TDZ。
1 | if (true) { |
是不是很神奇,如果 js 解释器逐句解释,在函数作用域内,不解释到最后一句,都无法知道会发生什么。
如果在 let 和 const 作用域之外使用该变量则不会报错。
1 | console.log(value) // undefined |
循环绑定
var 声明使得在循环中创建和使用函数总是有一些问题。比如想要每一秒输出一个递增的数字
1 | for (var i = 0; i < 5; i++) { |
本来预期输出 0 - 4,结果输出了 5 个 5。
1 | // 在 let 和 const 出现之前是使用闭包 |
let 在循环中每一次都创建一个新的变量,并且将其初始化为当前 i 的值,所以循环内部创建的每个函数都能得到 i 值得副本。for-in 循环和 for-of 循环也是一样的。
而 const 不能用于下面的循环,由 const 声明的 i 为常量,当对齐运行 ++ 运算的时候报错。
1 | for (const i = 0; i < 5; i++) { //TypeError: Assignment to constant variable. |
for-in 循环和 for-of 循环由于都是创建新的变量将其绑定为当前迭代值,所以不会出现上述问题
1 | const aa = [0, 1, 2, 3, 4, 5] |