JS中的var、let与const

定义

var 声明一个变量,可选地将其初始化为一个值。var定义
let 声明一个块级作用域的本地变量,并且可选的将其初始化为一个值。let定义
const常量是块级作用域,很像使用 let 语句定义的变量。常量的值不能通过重新赋值来改变,并且不能重新声明。const定义

var与let

let声明的不提升(hoisting)

使用var声明变量,变量会发生提升。打印i,会显示i已被声明但无赋值。
而使用let声明变量,变量不会发生提升。打印j,会显示j没有被声明。

1
2
3
4
console.log(i); //undefined
var i
console.log(j); //Uncaught ReferenceError: j is not defined
let j

同一作用域let不能重复定义同一变量

1
2
3
4
5
6
var i = 1;
var i = 2;
console.log(i); //2
let j = 1;
let j = 2;
console.log(j); //Uncaught SyntaxError: Identifier 'j' has already been declared

let在作用域后消失

使用let声明的变量只在作用域内有效,作用域之后变量即消失

1
2
3
4
5
6
7
8
if (true) {
var i = 1;
let j = 0;
console.log(i); //1
console.log(j); //0
}
console.log(i); //1
console.log(j); //Uncaught ReferenceError: j is not defined

let的暂时性死区

如果区块中存在let和const命令,则这个区块对这些命令声明的变量从一开始就形成了封闭做作用域,只要在声明之前做任何使用,都会报错。这在语法上称为“暂时性死区”(temporal dead zone ,简称TDZ)。

1
2
3
4
5
var i = 1;
if (true) {
console.log(i); //Uncaught ReferenceError: i is not defined
let i = 2;
}

let声明的全局变量不属于顶层对象的属性

1
2
3
4
var i = 1;
console.log(window.i); //1
let j = 1;
console.log(window.j); //undefined

const

const定义的常量,必须赋值

1
const i; //Uncaught SyntaxError: Missing initializer in const declaration

其他属性同let

  • 同一作用域const不能重复定义同一变量
  • const在作用域后消失
  • const的暂时性死区
  • const声明的全局变量不属于顶层对象的属性

let在for循环中的使用

1
2
3
4
5
6
7
var a = [];
for (var i = 0; i < 5; i++) {
a[i] = function() {
console.log(i);
};
}
a[2](); // 5

变量ivar命令声明的,在全局范围内都有效,所以全局只有一个变量i。每一次循环,变量i的值都会发生改变,而循环内被赋给数组a的函数内部的console.log(i),里面的i指向的就是全局的i。也就是说,所有数组a的成员里面的i,指向的都是同一个i,导致运行时输出的是最后一轮的i的值,也就是5。

1
2
3
4
5
6
7
var a = [];
for (let i = 0; i < 5; i++) {
a[i] = function() {
console.log(i);
};
}
a[2](); // 3

变量ilet声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量,所以最后输出的是3。你可能会问,如果每一轮循环的变量i都是重新声明的,那它怎么知道上一轮循环的值,从而计算出本轮循环的值?这是因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算。

1
2
3
4
5
6
7
8
9
10
11
12
13
for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
})
}
// 5 5 5 5 5

for (let i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
})
}
// 1 2 3 4 5

在setTimeout执行时,匿名函数function(){console.log(i);}会查找当前运行环境中变量的值。var声明的变量i为全局变量,匿名函数中的i均指向的是这个全局的i所以会打印5 5 5 5 5。但是let声明的变量i只在当前运行环境有效,其值是每一个循环创建匿名函数时候的新的变量i,但这个变量会在上一轮循环的基础上进行计算。所以会打印1 2 3 4 5。

引用来源:阮一峰. ECMAScript6入门