前言
引入原因:var存在变量提升
作用:强化对变量生命周期的控制
由于 JS 存在变量提升,比较容易误解,在 ES6 中引入了块级作用域
变量提升
先看下什么是变量提升:
变量的声明会被提升到函数顶部,而变量的赋值会在原处执行,所以可以先访问到该变量,其值为 undefined
注意是函数顶部
// 例1
console.log(myName) // undefined
var myName = 'Blackn'
// 相当于
var myName
console.log(myName)
myName = 'Blackn'
// 例2
function sayHello(say){
console.log(sayValue + '1') // undefined1
if(say){
var sayValue = 'Blackn'
console.log(sayValue) // Blackn
} else {
console.log(sayValue + '3') // undefined3
}
}
sayHello(true)
// 相当于
function sayHello(say){
var sayValue
console.log(sayValue + '1')
if(say){
sayValue = 'Blackn'
console.log(sayValue)
} else {
console.log(sayValue + '3')
}
}
块级声明(let、const)
块级作用域(词法作用域),存在于
- 函数内部
- 块中(
{ }内)
块之外无法访问块内声明的变量
let
let 与 var 用法类似,但是有 4 个主要区别
1、不存在变量提升
还是上面两个例子
// 例1
console.log(myName) // myName is no defined,报错
let myName = 'Blackn'
// 例2,let声明的只在当前作用域有效,即函数体内,或者更小一层,在{}内,这里是在if内
function sayHello(say){
console.log(sayValue + '1') // sayValue is not defined
if(say){
var sayValue = 'Blackn'
console.log(sayValue) // Blackn
} else {
console.log(sayValue + '3') // sayValue is not defined
}
}
sayHello(true)
例 2 中第一个 log 直接就报错,函数不会再往下执行,往下的两个打印是为了更好的理解
2、变量只在当前声明的作用域内生效
// 例1
if(true){
let myName = 'Blackn'
console.log(myName + '1') // Blackn1
}
console.log(myName + '2') // myName is not defined
// 例2
if(true){
let myName = 'Blackn'
if(true){
console.log(myName) // Blackn
}
}
// 例3
for (var i = 0; i < 5; i ++) {}
console.log(i) // 5
for (let i = 0; i< 5; i++ ) {}
console.log(i) // i is not defined
// for循环的 {} 里面也是一个块级作用域
注意看例 2 ,if 里面又套了个 if ,但是里面的 if 的作用域还是在外面的 if 的作用域里面,所以变量依旧生效
3、同一作用域内无法重复声明
function get() {
var myName = 'Blackn'
var myName = 'Jack'
} // 正常运行
function say() {
let myName = 'Blackn'
let myName = 'Jack'
} // 报错
同一作用域内无法重复声明,如果在上面的 say 函数外面声明 myName 就没有问题,或者在里面来个 if 判断,在判断内声明也没问题
4、暂时性死区
JS 引擎在扫描代码发现变量声明时,只有两种情况
- 遇到 var 声明,将声明提升到作用域顶部
- 遇到 let、const,将声明放到 TDZ (暂时性死区 temporal dead zone)中,访问 TDZ 中的变量会报错
执行过变量声明语句后,变量才会从 TDZ 中移除
if(true) {
console.log(typeof sayValue) // 报错,此时sayValue在暂时性死区内,访问会报错
let sayValue = 'Blackn'
}
console.log(typeof sayValue) // undefined,此时sayValue是在声明它的块级作用域外,所以不在暂时性死区内
if(true) {
let sayValue = 'Blackn'
}
const
const 用来声明常量,值一旦被设定则无法再被更改,声明时必须赋值
其他的都与 let 相同
需要注意的一个点是,如果 const 声明的是一个对象,可以修改对象的属性
const age = 21
age = 22 // Assignment to constant variable,报错
const person = {
age: 21
}
person.age = 22
person.name = 'Blackn'
console.log(person) // {age: 22, name: "Blackn"}
person = {} // 报错
const声明不允许修改绑定,但允许修改值,在声明对象后,可以修改该对象的属性值
总结
对比下 var、let、const的不同
- var:存在变量提升,无块级作用域,同一变量名可重复声明
- let:不存在变量提升,存在暂时性死区,有块级作用域,不可重复声明
- const:不存在变量提升,存在暂时性死区,有块级作用域,不可重复声明,声明时值必须赋值,且不能再次赋值(可以修改绑定的值,如对象的属性值)