ES6的块级作用域(闭包)

ES5只有全局作用域和函数作用域,没有块级作用域,这带来了一些不合理的场景。
场景一,内层变量可能会覆盖外层变量。

var tmp = new Date()
function f() {
  console.log(tmp)
  if (false) {
    var tmp = 'hello world';
  }
}
f() // undefined

场景二,用来计数的循环变量泄露为全局变量。

var s = 'hello';
for (var i = 0; i < s.length; i++) {
  console.log(s[i])
}
console.log(i) // 5

为了解决以上问题,ES6提出了块级作用域。何为块级作用域呢,简单来说,就是一个{ }就是一个块级作用域。
让我们来看一个例子:

var list = []

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

list[2]() // 5

如果变量使用的是var声明,每一次打印出来的都会是5,而不是当前值,这是为什么呢?其实上面代码相当于下面这样:

var list = []

// for循环只是为数组添加了function元素,但是并没有为function里面的i绑定当前的循环值
var i = 0;
list[0] = function() {
  console.log(i)
}
i++; // 1
list[1] = function() {
  console.log(i)
}
i++; // 2
list[2] = function() {
  console.log(i)
}
i++; // 3
list[3] = function() {
  console.log(i)
}
i++; // 4
list[4] = function() {
  console.log(i)
}
i++; // 5

// 注意这个时候i已经变成了5
list = [
  function() { console.log(i) },
  function() { console.log(i) },
  function() { console.log(i) },
  function() { console.log(i) },
  function() { console.log(i) }
]

// 函数执行是没有传参的,调用的是全局的i
list[2]() // 5

以上可见,list数组里的每个函数调用的都是全局的i,而非当前循环的i,而又因为JS是从上到下执行的,所以最终打印的就是所有循环结束后的i
那么应该如何来解决这个问题呢?其实很简单,回到我们的开头,ES5只有全局和函数2个作用域,而这种情况是因为函数读取了全局变量导致的,那我们在它读取全局变量之前覆盖它,或者给一个局部(函数)变量不就行?是的,核心思想就是这样,不过有几种不同的实现方式。
方法一,ES5的问题依然由ES5解决,这个时候就需要用到鼎鼎有名的闭包了。

var list = []

for (var i = 0; i < 5; i++) {
  (function(temp) {
    list[i] = function() {
      console.log(temp)
    }
  })(i)
  // }(i)) 也一样
}

list[2]() // 2

其实很简单,就是在list[i]被赋值之前,在它的外层包裹一个新的函数,这个函数是一个立即执行函数IIFE,它会创建一个局部(函数)变量temp,而list[i]这个时候改为调用temp,所以结果就可以跟当前的循环值对应起来。
方法二,使用ES6的新特性,let关键字创建一个块级作用域。

var list = []

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

list[2]() // 2

上面代码相当于下面这样:

var list = []

{
  let i = 0;
  list[i] = function() { console.log(i) }
}
{
  let i = 1;
  list[i] = function() { console.log(i) }
}
{
  let i = 2;
  list[i] = function() { console.log(i) }
}
{
  let i = 3;
  list[i] = function() { console.log(i) }
}
{
  let i = 4;
  list[i] = function() { console.log(i) }
}

list[2]() // 2

它就是每次循环都创建了一个新的变量,而这个变量处于块级作用域之内,所以不会跟其他的混乱。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容