ES6中let的实现

还只会用let,不好奇怎么实现的么?好奇就一起来看看吧

Featured image

  之前看到过一篇博客写ES6let的实现,没太在意,最近闲来搜了一下也看了几篇博客,在这贴一些代码

  ES6出来之前,作为菜鸟的我遇到下面这种,就会抱怨JS真是满满的坑

`use strict`
var a = []

for(var i = 0;i<5;i++){
    a[i] = function(){
        console.log(i)
    }
}

for (var k of a){
    k();
}

// 我们想在 console.log 中打印 0 ~ 4,而事实打印出来的却是 5 个 5

  现在使用 ES6let 就可以了

var a = []

for(let i = 0;i<5;i++){
    a[i] = function(){
        console.log(i)
    }
}

for (var k of a){
    k();
}

  为什么let可以很轻松的达到呢,如果不使用let那可以用什么别的方法吗?如下:

`use strict`
var a = [];

for(var i = 0; i < 5; i++) {
    _loop(i)
}

function _loop(i) {
    a[i] = function(){
        console.log(i)
    }
}

for(var k of a) {
    k()
}

  这是使用babel将上面let方法polyfill的版本,用以支持不支持ES6语法的浏览器。可以发现,是使用了一个函数将i的值存储下来达到块级作用域的目的。这样临时的变量i就得以保存下来

  使用 try catch方法

`use strict`

var a = []

for(var i = 0; i < 5; i++) {
    try {
        throw i
    } catch(e) {
        a[e] = function(){
            console.log(e)
        }
    }
}

for (var k of a){
    k();
}

  使用自执行函数的方法

`use strict`

var a = []

for(var i = 0; i < 5; i++) {
    (function(i){
        a[i] = function() {
            console.log(i)
        }
    })(i)
}

for (var k of a){
    k();
}

  使用map函数

`use strict`

var a = []

for(var i = 0; i < 5; i++) {
    [i].map((value) => {
        a[value] = function() {
            console.log(value)
        }
    })
}

for (var k of a){
    k();
}

  可以发现,在这些方法里,除了try catch以外都是使用函数的作用域来保存i的值


  上面介绍是在循环中使用的实例,那么在非循环中使用又是怎样的实现方法呢

var name = 'World!';

(function(){
    if(typeof name === 'undefined') {
        var name = 'Kit'
        console.log('Goodbye, ' + name)
    } else {
        console.log('Hello, ' + name)
    }
})()

  答案是 Goodbye Jack。首先JS引擎拿到这段代码会先进行变量提升,实际上的顺序应该是这样的

`use strict`
var name;
name = 'World!';

(function(){
    var name;
    if(typeof name === 'undefined') {
        name = 'Kit'
        console.log('Goodbye, ' + name)
    } else {
        console.log('Hello, ' + name)
    }
})()

外面的name实际上没有什么作用,主要是if里面的这个name。在某些编程语言里面,if实际上也是一个块级作用域,在块级作用域里面声明的变量,外面是不可以使用的。但是在JS里面,除了函数具备这个功能外再没有块级作用域了。所以在if里面声明的name会被提升到函数最顶部先行执行,然后判断name是不是undefined类型,因为没有赋值所以答案是true。所以name = Jack所以打印出Goodbye Jack

  如果题目是以下,答案又是什么呢

`use strict`
var name = 'World!';

(function(){
    if(typeof name === 'undefined') {
        let name = 'Kit'
        console.log('Goodbye, ' + name)
    } else {
        console.log('Hello, ' + name)
    }
})()

  因为使用的是let声明,没有变量提升。所以if判断里的name引用的是全局变量name,所以答案是Hello World

  简单的polyfill版本实现 let

`use strict`
var name = 'World'
(function(){
    if(typeof name === 'undefined') {
        (function() {
            var name = 'Kit'
            console.log('Goodbye, ' + name)
        })()
    } else {
        console.log('Hello, ' + name)
    }
})()

  可以看到,let的出现简化了大量的JS操作。特有的作用域弥补的JS块级作用域的短板。针对letpolyfill也多是使用函数的作用域来完成,由此可见函数对于JS的重要性


参考: