既然要说这三款声明语句的不同与相似,那就先来说说它们分别具有什么特性。
一,var
- 1,var声明的变量会被当做全局对象 window 的属性,也就是说,使用var声明变量会为全局对象 window/global创建一个新的属性,可以使用 window. 的方式访问变量的值。
var a = 22
console.log(a) // 22
console.log(window.a) // 22
- 2,前面提到var声明的变量会被当做 window 的属性,下面有以下几个问题。
- 那么在 局部作用域/函数作用域 使用 var 声明的变量 是否 也是这样呢?
- 在局部作用域内 对 同名变量 进行赋值 会不会影响 外部的同名变量?
- 如果 对 这个同名变量 取值,取的是哪里的值?
var a = 22
function A() {
a = 30
var b = 42
console.log(a) // 30
console.log(b) // 42
console.log(window.b) // undefined
}
console.log(a) // 22
console.log(window.a) // 22
A()
- 由上面代码可知,使用var在局部作用域创建的变量并不会添加到 全局对象上。
- 会影响,但影响的结果是什么样的得看 在哪里访问。本例中在函数内访问 a 结果为30,而在外部访问 a 结果为22,这里涉及到作用域链的概念。
(在访问变量的时候,会先在 同层作用域内 查找,如果在 同层内 没有找到,则会 向外一层(或者叫做父级作用域) 继续查找,以此内推,直到查找到变量为止,
如果到 顶层作用域 也没有查找到,则会报错 (Uncaught ReferenceError:... is not defined))
- 3,变量提升:即未定义,也可取。(由于var是不存在块级作用域的,所以 对于 var 来说,只有 全局作用域 和 局部作用域)
console.log(a) // undefined
var a = 10
或者 这样认为这段代码
var a
console.log(a) //此时a还未赋值,所以 系统给它了 undefined。
a = 10
- 4,重复声明:可以对同一个变量进行多次声明,会以最后一次的声明为结果
var a = 10
var a = 20
var a = 30
console.log(a) // 30
二,let
- 1, 不允许重复声明:不能对 同一标识符 进行 重复声明
let a = 10
let a = 20
console.log(a) // Uncaught SyntaxError: Identifier 'a' has already been declared
- 2, 不存在变量提升:变量的访问 不能 先于 声明
if (true) {
console.log(a) // Uncaught ReferenceError: Cannot access 'a' before initialization
let a = 10
}
- 3, 不属于 window全局对象:
let a = 10
console.log(window.a) // undefined
- 4,存在 暂时性死区:ES6规定,let/const 命令会使 区块 形成封闭的作用域。若在声明之前使用变量,就会报错。总之,在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”。 通俗点讲:暂时性死区 很霸道,只有把变量指定 过了 才可以使用。
let a = 10
if (true) {
a = 20
let a
} // Uncaught ReferenceError: Cannot access 'a' initialization
// 在这里并不会 因为 在全局作用域下 定义了 a,就可以在 块级作用域内 对 a 进行重新赋值。
// 因为在一个代码块内 使用 let语句声明的变量,该变量就只属于 该代码块了。
// 在代码块内,使用 let 命令 声明变量之前,该变量都是不可用的。
// 代码块:只要是 {} 大括号 包裹的代码 都可以称为代码块。
- 5,块级作用域:具有两点要素,一是let/const,二是代码块。
if (true) {
let a = 10
}
console.log(a) // Uncaught ReferenceError: a is not defined
// 使用 let声明语句 声明变量时 产生了封闭性区域(块级作用域),外部访问不到。
有无块级作用域对比
例一:
for (var i = 0; i < 5; i++) {
console.log(i) // 0,1,2,3,4
}
console.log(i) // 5
for (let j = 0; j < 5; j++) {
console.log(j) // 0,1,2,3,4
}
console.log(j) // Uncaught ReferenceError: a is not defined
例二:
for (var i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i) // 5,5,5,5,5
})
}
for (let j = 0; j < 5; j++) {
setTimeout(() => {
console.log(j) // 0,1,2,3,4
})
}
//产生这些区别的原因,就是因为 let 声明变量时 产生了块级作用域,且变量只属于 当前块级作用域,其它外部作用域访问不得。
三,const
- 1,不允许重复声明
- 2,不允许变量提升
- 3,不属于 window全局对象
- 4,暂时性死区
- 5,块级作用域 此五点特性是与 let 类似的,不再复述。
- 问:const 与 let 的区别是什么?
- 答:const声明的是 常量,不可变的 量,声明的标识符必须初始化。let 声明的是变量,是可以改变的量。
例一:
const a = 10
a = 20 // Uncaught TypeError: Assignment to constant variable
例二:
const obj = {
a: 'aa',
b: 'bb',
c: 'cc'
}
obj.a = 'aaaaaaaaa'
console.log(obj) // {a: 'aaaaaaaaa',b: 'bb', c: 'cc'}
例三:
const a // Uncaught SyntaxError: Missing initializer in const declaration
- 出现 例一,例二表现不一致的情况:const限制的是 它声明的变量初始化的值不能改变,而对于这个值,分为普通的值 和 引用地址。涉及到数据存储方式,栈存储 和 堆存储相关知识点,这里不再论述。
四,总结
-
const 与 let 的区别:const声明变量 必须初始化,且初始化的“值” 不可修改。日常使用中,const比较稳妥,const使用较多,尽量 避免使用 let。
-
const 和 let 的 出现主要引出了暂时性死区和块级作用域等概念,使代码的书写可更具规范性,可控性,使变量的作用范围 更加明确,避免了变量提升,重复声明等bug。