在es6之前,是没有块级作用域的,只有全局作用域和函数作用域,闭包也就是通过函数作用域摆脱全局作用域的影响,但是在es6中引入了块级作用域之后,就不需要那么麻烦了。
let、const声明
let声明
- let不会变量提升,而使用var声明变量,在预编译阶段都会提升到当前作用域的顶部。
- 使用let声明变量,会将变量的作用域限制在当前代码块(一对花括号)中。
- let禁止重复声明,同个代码块中不能使用let声明已有的同名变量。
const声明
const声明和let声明一致,但是const声明的是常量,const声明时必须初始化,且一旦设置不能再改变。
临时死区
function getValueTDZ(condition){
if(condition){
// 对未初始化和未声明的变量使用typeof都会返回undefined
console.log(typeof value)
let value = 1
}
}
getValueTDZ(true)
对于以上代码,我们会猜测返回undefined,但实际上会抛出错误:
在使用let声明value的块级作用域中,且在使用let声明value之前,value存在于临时死区。
循环中的块级绑定
for循环中有两层作用域
for循环里其实有两层作用域,循环条件是一层,循环体是一层。 循环条件中的使用let定义的值在for循环外面是访问不到,可以在循环条件和循环体中访问到。
// 条件中的i和里面的i是两个作用域
for (let i = 0; i < 2; i++) {
let i = 'foo'
console.log(i) //foo
}
// 改写成if的形式就好理解了
let i = 0
if (i < 2) {
let i = 'foo'
console.log(i)
}
i++
if (i < 2) {
let i = 'foo'
console.log(i)
}
i++
解决循环中的问题
var funcs = [];
for(var i=0;i<10;i++){
funcs.push(
function(){console.log(i);}
);
}
funcs.forEach(
function(func){func();}
);
我们会看到控制台打印了10个10,这是因为使用var声明的变量存在于全局作用域中,当执行函数的时候,沿着作用域链找到全局的i,已经循环完毕变成10了。
- 改进方法1:利用闭包加了一个自执行函数,创建了一层函数作用域
var funcs = [];
for (var i = 0; i < 10; i++) {
funcs.push(
// 自执行函数会马上执行,但是会在内层函数的作用域链上加一层闭包作用域,记录不同的i
(function (value) {
return function () {
console.log(value);
}
})(i)
);
}
funcs.forEach(
function(func){func();}
);
- 改进方法2:采用let替换var,let会创建块级作用域,每次声明的i都在不同的块级作用域中
var funcs = [];
for(let i=0;i<10;i++){
funcs.push(
function(){console.log(i);}
);
}
funcs.forEach(
function(func){func();}
);
在for循环的条件中不要使用const声明,因为一般我们会修改这个变量导致出错。
全局块级绑定
- 使用var在全局声明一个变量时,会挂载window对象上,可能会覆盖window对象的已有属性。
- 使用let或const在全局声明一个变量时,不会挂载window对象上。
能用const不用let,能用let不用var。