var 和 let 关键字
1. 属于顶层对象window
下面代码说明:
var
声明的全局变量时 window 的属性,是可以通过 window.变量名 的方式访问的。
而let
声明的全局变量不是 window 的属性,不可以通过 window.变量名的方式访问。
var a = 5;
console.log(window.a); // 5
let a = 5;
console.log(window.a); // undefined
2. 变量重复声明
下面代码说明:
var
可以重复定义,而let
不可以重复定义。
var a = 5;
var a = 6;
console.log(a) // 6
let a = 5;
let a = 6;
// 报错:VM131:1 Uncaught SyntaxError: Identifier 'a' has already been declared at <anonymous>:1:1
3. 变量提升
下述代码中, a 的调用在声明之前,所以它的值是
undefined
,而不是 Uncaught ReferenceError。实际上因为var
会导致变量提升
说明:var
关键字定义变量时,存在变量提升
。
而let
关键字不存在变量提升
。
console.log(a) // undefined
var a = 2;
// 等价写法
var a;
console.log(a) // undefined
a = 2;
function foo() {
console.log(a)
var a = 5
}
foo() //undefined
function foo() {
var a
console.log(a)
a = 5
}
foo() //undefined
function foo() {
console.log(a)
let a = 5
}
foo() // 报错:Uncaught ReferenceError: Cannot access 'a' before initialization
console.log(b) // 报错
let b = 2;
4. 暂时性死区
只要块级作用域内存在
let
命令,它所声明的变量就绑定在了这个区域,不再受外部的影响。
下面代码中,存在全局变量a
,但是块级作用域内 let 又声明了一个局部变量a
,导致后者绑定这个块级作用域,所以在let
声明变量前,对a
赋值会报错。
ES6 明确规定,如果区块中存在let
和const
命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
总之,在代码块内,使用let
命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”
var = 5;
if(true){
a = 6;
let a;
}
有时“暂时性死区”比较隐蔽,比如:
function foo(b = a, a = 2) {
console.log(a, b)
}
foo()
// Uncaught ReferenceError: Cannot access 'a' before initialization
5. 块级作用域
let
实际上为 JavaScript 新增了块级作用域
{
let a = 5;
}
console.log(a) // undefined
a
变量是在代码块{}
中使用 let 定义的,它的作用域是这个代码块内部,外部无法访问。
我们再看一个项目中很常见的 for 循环:
for (var i = 0; i < 3; i++) {
console.log('循环内:' + i) // 0、1、2
}
console.log('循环外:' + i) // 3
如果改成
let
会怎么样呢?
for (let i = 0; i < 3; i++) {
console.log('循环内:' + i) // 0、1、2
}
console.log('循环外:' + i) // ReferenceError: i is not defined
继续看下面两个例子的对比,这时
a
的值又是多少呢?
if (false) {
var a = 5
}
console.log(a) // undefined
if (false) {
let a = 5
}
console.log(a) // Uncaught ReferenceError: a is not defined
思考
请问
i
的值是多少?
for(var i = 0; i < 3; i++){
setTimeout(function() {
console.log(i)
})
}
// 输出结果:3、3、3
答案是3次3。
但我希望的值是0、1、2,也就是每次保存住循环时候 i 的值,应该如何做呢?
方案1:闭包
for(var i = 0; i < 3; i++){
(function(j){
setTimeout(function() {
console.log(j) // 0、1、2
})
})(i);
}
方案2:使用 let
for(let i = 0; i < 3; i++){
setTimeout(function() {
console.log(j) // 0、1、2
})
}
总结
- 使用let声明的变量:
- 不属于顶层对象window
- 不允许重复声明
- 不存在变量提升
- 暂时性死区
- 块级作用域