下面所有代码皆可按 F12,进入Console运行
作用
相同之处:声明变量
不同之处:声明变量类型、变量提升、变量的暂时性死区、变量重复声明、作用域
声明类型
- let 与 var 都能够声明任意类型变量;
- const 只能声明只读常量。对于复合类型的变量,变量名不指向数据,而是指向数据所在的地址,const命令只是保证变量名指向的地址不变,并不保证该地址的数据不变。
const a = 2
a = 4 // TypeError: Assignment to constant variable.
// 声明复合类型变量
const b = [1, 2, 3]
console.log(b) // [1, 2, 3]
b[1] = 4
console.log(b) // [1, 4, 3]
变量提升
- 使用 var 声明变量,变量使用与声明顺序没有要求
function f_var() {
console.log(a)
var a = 10
}
f_var() //undefined
- 注:undefined 表示变量已经声明,但没有值 *
- 使用 let/const 声明变量,变量一定要在声明后使用
function f_let() {
console.log(b)
let b = 10
}
f_let() // ReferenceError: Cannot access 'b' before initialization
function f_const() {
console.log(c)
const c = 10
}
f_const() // ReferenceError: Cannot access 'c' before initialization
暂时性死区
含义:如果只使用 var 声明的变量,由于 var 声明的变量存在变量提升,所以只要在区域内声明了变量,无论在任何位置都可以使用。如果在代码块内存在 let/const 命令声明的变量,那么所声明的变量就“绑定”(binding)这个区域,所以使用 let/const 命令声明变量之前,该变量都是不可用的。
function f_var() {
console.log(a)
if (true) {
console.log(a)
}
if (true) {
var a = 1
}
}
f_var() // undefined undefined
function f_let() {
var a = 1
if (true) {
console.log(a)
let a = 3
}
}
f_let() // ReferenceError: Cannot access 'a' before initialization
function f_const() {
var a = 1
if (true) {
console.log(a)
const a = 3
}
}
f_const() // ReferenceError: Cannot access 'a' before initialization
- 例子 f_var() 中,只有 var 声明变量,所以在只要声明了变量,在 f_var() 任何一个位置都可以使用。
- 例子 f_let 和 f_const 中,虽然在 if 区域外部使用了 var 声明了变量,但是由于 if 内部使用了 let/const 声明了同一个变量,所以变量会绑定该区域,所以在声明之前该变量都无法使用。
不允许重复声明
- 使用 var 声明变量,可多次声明同一名称变量
- 使用 let/const 声明变量,在同一作用域内,只能声明一次。
// SyntaxError: Identifier 'a' has already been declared
function f_let() {
var a = 1
let a = 1
}
// SyntaxError: Identifier 'a' has already been declared
function f_let() {
let a = 1
let a = 1
}
// SyntaxError: Identifier 'a' has already been declared
function f_let(a) {
let a = 2
}
// 不报错,具体原因情况变量作用域相关内容
function f_let(a) {
{
let a = 2
}
}
作用域
- ES5 只有全局作用域和函数作用域,所以使用 var 声明变量时,容易出现下面不合理的场景
// 场景一,内层变量覆盖外层变量
var tmp = 'outside'
function f() {
console.log(tmp)
if (false) {
var tmp = "inside"
}
}
f() // undefined; 由于变量提升,导致内层的tmp变量覆盖了外层的tmp变量。
// 场景二: 用来计数的循环变量泄露为全局变量
var s = 'outside'
for (var i = 0; i < s.length; i++) {
console.log(s[i])
}
console.log(i) // 5; 变量 i 在循环结束后,泄露成了全局变量。
- ES6中的 let/const 为 JavaScript 新增加块级作用域
function f1() {
let n = 5
if (true) {
let n = 10
}
console.log(n) // 5; 内层 if 的作用域变量不影响外部变量
}
{
{let insane = 'Hello World'}
console.log(insane) // 报错, 变量在内部声明,无法在外部使用
};
{
let insane = 'Hello World'
{let insane = 'Hello World'} // 不报错,内层作用域可以定义外层作用域的同名变量。
}