介绍ECMAScript 6
新引入的块级作用域绑定机制及其最佳实践
变量提升(Hoisting):在函数作用域或全局作用域通过关键字var声明的变量,无论实际上是在哪里声明的,都会被当成在当前作用域顶部声明的变量
function getValue () {
if(condition) {
var value = "blue"
}
else {
// 此处可以访问value,为undefined
return null
}
}
变量value的声明被提升至函数顶部,而初始化操作依然留在原处执行
为此,ECMAScript6
引入块级作用域来强化对变量生命周期的控制
块级声明用于声明在指定块的作用域之外无法访问的变量
亦称为词法作用域,存在于
- 函数内部
- 块中(字符{}之间的区域)
用法与var相同,用let替代var来声明变量,就可以把变量的作用域限制在当前代码块中
let声明不存在变量提升,因此,一般将let声明语句放在封闭代码块的顶部,以便整个代码块都可以访问
function getValue () {
if(condition) {
let value = "blue"
}
else {
// value不存在
return null
}
}
代码块执行完,变量立刻被销毁
如果作用域中已经存在某个标识符,若再使用let声明则会报错
var count = 30
let count = 40 // 报错
即同一作用域中不能用let重复定义已经存在的标识符
如果作用域中嵌套另一个作用域,则可以再内嵌的作用域中用let声明同名变量
var count = 30
if(condition) {
let count = 40
}
内部块中的count会遮蔽全局的count
const
关键字,使用const
声明的是常量,必须进行初始化,其值被设定后不可更改
const maxItems = 30
const name // 错误
maxItems = 0 // 错误
const
与let声明的都是块级标识符,常量也只在当前代码块内有效,执行到块外会立即销毁
在同一作用域用const
声明已经存在的标识符也会导致语法错误,无论该标识符是var、let或const
声明的
var message = "Hello"
let age = 25
// 报错
const message = "bye"
const age = 30
const
声明对象,可以修改对象的属性值
const person = {
name: "Nicholas"
}
// 正确
person.name = "Tom"
// 错误
person = {
name: "Tom"
}
Temporal Dead Zone
用var声明的变量会变量提升,将其提升到作用域顶部
用let或const
声明的变量,将其放到TDZ
中,访问TDZ
中的变量会报错,执行变量声明语句后,变量才会从TDZ
中移出,可以正常访问
// 正常 undefined
console.log(typeof value)
var value = 1
// 报错
console.log(typeof a)
let a = 1 // const a = 1
for(var i = 0; i < 10; i++) {
}
console.log(i) // 10
由于变量提升,循环结束后仍可访问i
for(var i = 0; i < 10; i++) {
}
console.log(i) // 报错
使用let声明的i只在循环内有效,执行完for循环后,i立刻被销毁
var funcs = []
for(var i = 0; i < 10; i++) {
funcs.push(function () {
console.log(i)
})
}
funcs.foreach(function(func) {
func() // 输出10次10
})
循环里的每次迭代同时共享着i,循环内部创建的函数全都保留了对相同变量的引用,循环结束时变量i的值为10,因此每次调用都会打印10
可以使用立即执行函数优化
var funcs = []
for(let i = 0; i < 10; i++) {
funcs.push(function () {
console.log(i)
})
}
funcs.foreach(function(func) {
func() // 输出0-9
})
每次循环时let声明都会创建一个新变量i,每次创建的函数都能获得单独的变量i
标准中未明确指明不允许在循环中使用const
声明
当循环中不会修改该变量时,可以使用const
声明
var obj = {
a : 1,
b : 2,
c : 3
}
for(const key in obj) {
//
}
在全局作用域中使用var声明某个变量时,会创建一个新的全局变量挂载到全局对象(window对象),所以可能会无意中覆盖一个已经存在的全局属性
在全局作用域中使用let或const
,不会添加为全局对象的属性