深入理解ES6-1.块级作用域

52 阅读1分钟

在es6之前,是没有块级作用域的,只有全局作用域和函数作用域,闭包也就是通过函数作用域摆脱全局作用域的影响,但是在es6中引入了块级作用域之后,就不需要那么麻烦了。

let、const声明

let声明

  1. let不会变量提升,而使用var声明变量,在预编译阶段都会提升到当前作用域的顶部。
  2. 使用let声明变量,会将变量的作用域限制在当前代码块(一对花括号)中。
  3. let禁止重复声明,同个代码块中不能使用let声明已有的同名变量。

const声明

const声明和let声明一致,但是const声明的是常量,const声明时必须初始化,且一旦设置不能再改变。

临时死区

function getValueTDZ(condition){
    if(condition){
        // 对未初始化和未声明的变量使用typeof都会返回undefined
        console.log(typeof value)
        let value = 1
    }
}
getValueTDZ(true)

对于以上代码,我们会猜测返回undefined,但实际上会抛出错误: image.png 在使用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. 改进方法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();}
);
  1. 改进方法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声明,因为一般我们会修改这个变量导致出错。

全局块级绑定

  1. 使用var在全局声明一个变量时,会挂载window对象上,可能会覆盖window对象的已有属性。
  2. 使用let或const在全局声明一个变量时,不会挂载window对象上。

能用const不用let,能用let不用var。