块级作用域
es6新引入了申明变量的四种方式:let const class import
。
引入let
- 基本用法
let
声明变量与var
很类似,只是let
声明的变量只能在当前所在作用域有效,且变量的使用只能在所在变量声明之后。
{
console.log(a) // 报错
let a = 1
}
- 引入原因
es6之前版本的js只有全局作用域和函数作用域,这意味着通过
var 函数声明
(es6版本之前的两种变量声明方式)声明的变量其作用域是整个函数体。即函数在运行之前,首先会创造运行于此函数体内的所有变量。这就是所谓的变量提升
。 缺乏块级作用域
和存在变量提升
导致了什么问题?
- 原本在块级作用域内声明的变量在块级作用域外起了作用,这使得编程变得混乱。
function foo () {
console.log(baz) // => undefinded
var baz = 0
if (true) {
var baz = 1
}
console.log(baz) // => 1
}
for
循环中变量过渡共享,不符合编程逻辑
var arr = []
for (var i = 0; i < 10; i++) {
setTimeout(console.log(i), 1000) // 输出10次9
arr[i] = function () {
console.log(i)
}
}
arr[0]() // => 9
- 顶层对象属性与全局变量挂钩,导致:难以模块化编程;编译时不能发现未声明的变量;开发者很容易不小心创造全局变量;
a = 1 // 对象的属性是动态创造的,运行到此步时才会创建
console.log(window.a) // => 1
nk = 0 // 不小心打出的字母,将不报错,并成为全局变量
<script>
var a = 1 // 全局对象声明的变量会挂载到顶层对象上
</script>
<script>
console.log(a) // => 1
</script>
为了解决上述问题es6正式引入了let
变量的声明方式。let
究竟能解决什么问题呢?
- 禁止变量提升(指的是变量不能提升使用,但实质也提升了声明)
{
console.log(a) // ReferenceError
let a = 1
}
- 采用
TDZ
临时性死区的方式引入块级作用域let
声明的变量,会提升至作用域的顶部声明,但在运行到此变量声明语句前,此变量不能使用,即处于临时性死区。避免了外层作用域的同名变量在此层变量声明前可以使用而导致的错误。由此而引入了块级作用域。也就是说let
声明的变量,作用域是外层块,而不是外层函数。
{
let a = 1
{
console.log(a) // ReferenceError
let a = 2
}
}
- for(let x...)循环,对每次循环绑定作用域
// 对于`let`的循环,会在最外层循环单独创建一个作用域,同时对每次循环也创建一个作用域
for(let i = 0; i < 10; i++) {
console.log(i)
}
// 等价于
{
let i = 0;
if (i < 10) {
let j = i;
console.log(j)
i = i + 1
}
...
}
- 声明变量不与全局对象挂载
var function
声明的变量仍然与顶层关联,但let const class import
新语法声明的变量,不再与顶层对象关联。
- 缺陷及未来
let
重定义报错 不同于var
,当你对let
重复声明同名变量时,会报错(包括多个脚本声明同名变量)。因此,若你想在不同脚本使用同名变量而不受干扰,最好使用es6的模块机制。
function (arg) {
let arg // 报错
}
let
是严格模式下的保留词 使用var let = 0;
在非严格模式下并不会报错,虽然我想你也不会蠢到写这种语法。
引入const
const
声明的变量与let
一样,同样具有块级作用域。不同的是,const
声明的是常量,必须在声明的时候赋值,否则报错。
const syntaxAnylaze; // SyntaxError
注意
const
声明的常量是指声明是所对应的的内存地址值是不可改变的,也就是说声明的变量是原始类型string number boolean
那么变量的值是不可改变的,但如果声明的变量是对象类型Object Symbol Array
,则变量值是可变的,但指向变量的指针(引用)是不可变的。
const a = 0
a = 1 // 报错
const b = {}
b.error = 'noerror' // 正确
b = {} // 报错
引入class
class
声明的类不同于传统的function
声明语法,class
声明的语法与let
保持一致:1. 不可重复定义 2. 禁止变量提升 3. 只能在模块的顶层作用域声明
全局对象的不统一
全局变量在不同环境下的表示是不一致的:
node --- global(this会返回顶层模块)
window --- self window frame this
work server --- self
es联盟为了保持统一,在es2020语言标准层面,提出使用globalThis
表示全局变量。
变量的重复声明
let const import class
方式变量的重复声明会报错var
方式变量的重复声明 会忽略再生声明,而变成赋值。
var a = 1
var a = 2 // => a = 2 不报错
var b =2
var b // 直接忽略(变量提升的缘由)
参考文献
- 阮一峰的《es6入门》es6.ruanyifeng.com/#docs/let
- 《深入浅出es6》系列文章