美文网首页
ES6系列一:let、const

ES6系列一:let、const

作者: 原型设计 | 来源:发表于2019-07-20 12:14 被阅读0次

注意: 所有代码都是用ES6在线测试,分享给大家。

babel.png

经典面试题:

var funcs = [];
for (let i = 0; i < 3; i++) {
    funcs[i] = (function(i){
        return function() {
            console.log(i);
        }
    }(i))
}
funcs[0](); // 0

解决方案 let:

很多人都知道可以用let来解决输出1、2、3,为什么可以?下面我们讲解这里面的原理。

我们知道let 声明的变量不存在变量提升、重复声明、不能绑定全局作用域等等特性,可是为什么在这里就能正确打印出 i 值呢?

如果是不重复声明,在循环第二次的时候,又用 let 声明了 i,应该报错呀,就算因为某种原因,重复声明不报错,一遍一遍迭代,i 的值最终还是应该是 3 呀,有人说 for 循环的设置循环变量的那部分是一个单独的作用域,就比如:

for (let i = 0; i < 3; i++) {
  let i = 'abc';
  console.log(i);
}
// abc
// abc
// abc

如果我们把 let 改成 var 呢?

for (var i = 0; i < 3; i++) {
  var i = 'abc';
  console.log(i);
}
// abc

为什么结果就不一样了呢,如果有单独的作用域,结果应该是相同的,但是不相同。

我们查看 ECMAScript 规范第 13.7.4.7 节:

es.png

我们会发现,在 for 循环中使用 let 和 var,底层会使用不同的处理方式。

那么当使用 let 的时候底层到底是怎么做的呢?

简单的来说,就是在 for (let i = 0; i < 3; i++) 中,即圆括号之内建立一个隐藏的作用域,这就可以解释为什么:

for (let i = 0; i < 3; i++) {
  let i = 'abc';
  console.log(i);
}
// abc
// abc
// abc

然后每次迭代循环时都创建一个新变量,并以之前迭代中同名变量的值将其初始化。这样对于下面这样一段代码:

var funcs = [];
for (let i = 0; i < 3; i++) {
    funcs[i] = function () {
        console.log(i);
    };
}
funcs[0](); // 0

就相当于:

// 伪代码
(let i = 0) {
    funcs[0] = function() {
        console.log(i)
    };
}

(let i = 1) {
    funcs[1] = function() {
        console.log(i)
    };
}

(let i = 2) {
    funcs[2] = function() {
        console.log(i)
    };
};

如果将let 改成const ,会出现什么情况呢?答案一想就知道:每次迭代的过程中去修改const 定义的值是不允许的。

说完了普通的 for 循环,我们还有 for in 循环呢~

var funcs = [], object = {a: 1, b: 1, c: 1};
for (var key in object) {
    funcs.push(function(){
        console.log(key)
    });
}

funcs[0]()

结果是 'c';

那如果把 var 改成 let 或者 const 呢?

使用 let,结果自然会是 'a',const 呢? 报错还是 'a'?

结果是正确打印 'a',这是因为在 for in 循环中,每次迭代不会修改已有的绑定,而是会创建一个新的绑定。

编译let:

我们再写个直观的例子:

let value = 1;
{
    let value = 2;
}
value = 3;
var value = 1;
{
    var _value = 2;
}
value = 3;

本质是一样的,就是改变量名,使内外层的变量名称不一样。

那像 const 的修改值时报错,以及重复声明报错怎么实现的呢?

其实就是在编译的时候直接给你报错……

那循环中的 let 声明呢?

var funcs = [];
for (let i = 0; i < 10; i++) {
    funcs[i] = function () {
        console.log(i);
    };
}
funcs[0](); // 0

Babel 巧妙的编译成了:

var funcs = [];

var _loop = function _loop(i) {
    funcs[i] = function () {
        console.log(i);
    };
};

for (var i = 0; i < 10; i++) {
    _loop(i);
}
funcs[0](); // 0

这也是为什么可以用必包解决这个问题?

var funcs = [];
for (var i = 0; i < 10; i++) {
    (function (i) {
        funcs[i] = function () {
          console.log(i);
        };
    })(i);
}
funcs[0](); // 0

相关文章

网友评论

      本文标题:ES6系列一:let、const

      本文链接:https://www.haomeiwen.com/subject/yfbylctx.html