作为ES6新特性之一的let,其与var的区别、其实现原理又是怎样的呢?
谈到var与let,不可避免的要提到JS的作用域。
众所周知在JS中不像其他语言一样有块级作用域,它只有全局作用域及函数内部的局部作用域
var 是全局作用域,有变量提升的作用
下面是一道经典面试题,其结果是 // 5 5 5 5 5
![]()
因为var全局作用域,所以在for循环中声明的 i 就是一个全局变量,全局可用,每一次给 i 赋值都是给全局变量 i 赋值。
而setTimeout是异步事件(属于eventloop中的宏任务),所以在循环过程中setTimeout会进入事件队列等待时机执行,也就是在for循环执行结束之后才会执行,for循环执行完之后全局变量 i = 5,所以结果就是 // 5 5 5 5 5
那我们如何在for循环中同时使用setTimeout的时候得到我们想要的结果呢? // 0 1 2 3 4
实现这个结果方法很多
方法一 调用函数
如上采用函数调用的形式,因为函数调用属于同步任务,在执行for循环的过程中调用varFun同时把 i 的值传递给函数,在函数内部参数 i 就变成了函数内的局部作用域,varFun中的setTimeout访问的就是varFun内的参数,从而解决全局变量的问题。
方法二 立即执行函数
如上采用的就是在for循环中定义立即执行的匿名函数,同时把参数 i 的值绑定到匿名函数的作用域内,从而实现规避全局变量的问题。
方法三 把 var 改成 let
我们只需要把for循环中的var改成let,即可实现输出结果为 // 0 1 2 3 4
因为let声明的是块级作用域,只能在代码块中使用,在js中一个{}我们也称之为一个代码块,每次循环会产生一个代码块,每个代码块中的都是一个新的变量 i 。
经典面试题
答案是 // Hello李四
在js拿到这段代码首先会进行变量提升,实际提升之后代码是下面这个样子
外面的name实际上并没有起作用,主要是if里面的这个name。
在某些编程语言里面,if实际上也是一个块级作用域,在块级作用域里面声明的变量外面是不可以使用的。但是在JS里面,除了函数具备这个功能外再没有块级作用域了。所以在if里面声明的name会被提升到函数最顶部先行执行,然后判断name是不是undefined类型,因为没有赋值所以答案是true,所以打印出Hello李四。
改进代码
结果为 // Goodbye张三
因为使用的是let声明,没有变量提升。所以if判断里的name引用的是全局变量name,所以答案是 // Goodbye张三
let的实现原理
通过采用匿名函数的形式把声明的变量变成局部作用与可用的变量,而不是全局变量,实现与var的区分
实现代码