美文网首页
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