ECMAScript变量是松散类型的,变量可以用于保存任何类型的数据,每个变量不过是一个用于保存任意值的命名占位符。有3个关键字可以声明变量,var、let和const。其中let和const是ES6新增关键字。
var关键字
1、var声明作用域
使用var定义的变量会成为包含它的函数的局部变量
function test() {
var name = 'Mike'
console.log(name) // 'Mike'
}
console.log(name) // ReferenceError: name is not defined
2、var声明提升
使用var时,下面的代码不会报错,因为用var声明的变量会自动提升到函数作用域顶部:
function test() {
console.log(name)
var name = 'Mike'
}
test() // undefined
之所以不会报错是因为ECMAScript运行时把它看成等价于如下的代码:
function test() {
var name
console.log(name)
name = 'Mike'
}
test() // undefined
可以多次使用var声明同一个变量
var age = 10
var age = 20
var age = 30
console.log(age) // 30
let关键字
let和var最明显的区别是:let声明的范围是块作用域,var声明的范围是函数作用域
if (true) {
var name = 'Mike'
console.log(name) // Mike
}
console.log(name) // Mike
if (true){
let name = 'Mike'
console.log(name) // Mike
}
console.log(name) // ReferenceError: name is not defined
let不允许同一个块作用域中出现冗余声明,会报错:
let age = 10
let age = 20
console.log(age) // SyntaxError: Identifier 'age' has already been declared
对声明冗余报错不会因混用let和var而受影响
var name
let name
console.log(name) // Identifier 'name' has already been declared
let name
var name
console.log(name) // Identifier 'name' has already been declared
暂时性死区
let和var的另一个重要区别是let声明的变量不会在作用域中被提升
console.log(name)
let name = 'Mike' // ReferenceError: Cannot access 'name' before initialization
在解析代码时,js引擎会注意到后面的let关键字,也会给变量分配空间,但在此之前不能用任何方式来引用未声明的变量。在let声明之前的执行瞬间被称为 “暂时性死区”。
for循环中的let声明
for循环中使用var定义的迭代变量会渗透到循环体外部
for(var i = 0; i < 5; i++){}
console.log(i) // 5
改成let声明后这种情况就消失了
for(let i = 0; i < 5; i++){}
console.log(i) // ReferenceError: i is not defined
使用var时最常见的问题是对迭代变量的奇特声明和修改
for(var i = 0; i < 5; i++){
setTimeout(() => {
console.log(i)
}, 0)
} // 5,5,5,5,5
输出的值并不是0, 1, 2, 3, 4
,因为在退出循环时迭代变量保存的是导致退出循环的值5。之后执行超时逻辑时,所有的i都是同一个变量。而let声明迭代变量时js引擎会为每个迭代循环声明一个新的迭代变量。每个setTimeout
引用的都是不同的变量。
for(let i = 0; i < 5; i++){
setTimeout(() => {
console.log(i)
}, 0)
} // 0, 1, 2, 3, 4
const关键字
const和let基本相同,唯一一个重要的区别是它声明变量时必须同时初始化,且不能修改
const name = 'Mike'
name = 'Lily' // TypeError: Assignment to constant variable.
// 不允许重复声明
const name = 'Mike'
const name = 'Lily' // SyntaxError: Identifier 'name' has already been declared
// const声明的作用域也是块
if (true) {
const name = 'Mike'
console.log(name)
}
console.log(name) // ReferenceError: name is not defined
const声明到应用到顶级原语或者对象。也就是说赋值为对象的const变量不能再被重新赋值为其他引用值,但对象的键不受限制
const obj1 = {}
obj1 = {} // TypeError: Assignment to constant variable.
const obj2 = {}
obj2.name = 'Mike'
console.log(obj2) // { name: 'Mike' }
如果想让整个对象不能修改,可以使用Object.freeze()
。即使改变对象属性不会报错,但是会失败。
const obj2 = Object.freeze({})
obj2.name = 'Mike'
console.log(obj2) // {}