閉包和let塊級作用域


還是先從一個題目開始:

寫一個隔1s輸出數組的一項的函數。

如果可以用ES6語法,則可以這么寫:

function print (arr) {
    for (let i = 0; i < arr.length; i++) {
        setTimeout(() => {
            console.log(arr[i])
        }, 1000 * i);
    }
}

但是如果把這里的let改成var,則輸出就會變成一連串的undefined

有同學很快想到了這是閉包啊,因為setTimeout把函數加入到microqueue中,所以等到setTimeout的函數體執行時,i已經走完了for循環,變成了arr.lengtharr[arr.length]顯然是undefined。

簡單修改一下,變成ES5的語法。

function print (arr) {
    for (var i = 0; i < arr.length; i++) {
        (function (index) {
            setTimeout(() => {
                console.log(arr[index])
            }, 1000 * index);
        })(i);    
    }
}

其實就是利用閉包是向父級作用域尋找值的特性,給i包裝一層作用域,把i存起來。

閉包概念還請翻看之前的一篇blog-閉包和類

到這里閉包的理解應該差不多了,而今天的關鍵點在於——

let做了什么?

阮一峰老師的《ECMAScript 6》入門里給出[定義](http://es6.ruanyifeng.com/#docs/let

ES6 新增了let命令,用來聲明變量。它的用法類似於var,但是所聲明的變量,只在let命令所在的代碼塊內有效。

他提到了let的幾個特性:

  1. 只存在於塊級作用域中

  2. 不存在變量提升

  3. 暫時性死區

  4. 不允許重復聲明

這里我不再贅述,大家可以仔細閱讀一下阮一峰老師的書。

我更感興趣的是,在ES5的語法中,如何模擬let這種塊級作用域的效果。這個時候,應該讓babel出場了。

打開這個鏈接:可以看到轉換后的代碼。

"use strict";

function print(arr) {
  var _loop = function _loop(i) {
    setTimeout(function () {
      console.log(arr[i]);
    }, 1000 * i);
  };

  for (var i = 0; i < arr.length; i++) {
    _loop(i);
  }
}

其實可以對比發現,babel轉換后的代碼和我們上面寫的ES5實現其實是一樣的。

大概就是通過對let綁定的塊級作用域加一個函數,把let聲明的參數,通過函數傳入,達到塊級作用域的效果。

大家可以在babel試一下let的其他特性,轉移出的ES5語法並不能實現有的特性,比如暫時性死區。

完,感謝閱讀。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM