JavaScript--深入淺出之閉包

本文很多理論基礎以及概念來自於《你不知道的JavaScript》,感謝KYLE SIMPSON先生。首先要說明,閉包是和作用域息息相關的。

function foo(){
  var a = 2;
  function bar(){console.log(a)}
  return bar;
}
var baz = foo();
baz();  //2
這段代碼,函數bar()被當做一個值類型進行傳遞,這個時候就產生了閉包。這裏呢,bar()聲明的位置來看,他擁有訪問foo()內部作用域的閉包。也就是bar()對該作用域的引用。

強調一下概念,(訪問自身作用域【函數或塊作用域】)的函數被傳遞,就會產生閉包在這些函數中的應用。

通俗點說,這個函數能夠讀取到其他函數的內部變量,並且在這個函數的詞法作用域之外被執行了。

下面概念清晰了,讓我們來接觸一道經典的閉包面試題:

for (var i = 0; i < 5; ++i) {
     setTimeout(function () {
             console.log(i + ' ');
        }, 100);
}
屁話不多說,執行結果五個六。

背景知識:setTimeout註冊的函數需要等待線程空閒才能執行,也就是for循環執行之後。
所以即使是setTimeout(..,0)輸出結果也不會變。現在的問題就是,當函數執行的時候,訪問的都是同一個i,所以我們要將函數自己圈起來一個閉包。

for (var i = 0; i < 5; ++i) {
      (function () {
            setTimeout(function () {
                  console.log(i + ' ');
            }, 100);
      }());
}
這樣,通過這個IIFE函數確實包起來一個作用域,但是這個作用域卻沒有什麼內容,那我們給他加點內容進去。
for (var i = 0; i < 5; ++i) {
      (function (i) {
            setTimeout(function () {
                  console.log(i + ' ');
            }, 100);
      }(i));
}
加了個i,這樣內存中就會有五個i,分別存在於函數的作用域中。

es6語法推出了塊作用域,還可以通過將var 改成let來操作。

for (let i = 0; i < 5; ++i) {
     setTimeout(function () {
             console.log(i + ' ');
        }, 100);
}
let在for循環中,每次循環都會聲明一次,for循環了5次,在每個函數作用域中都存在一個自己的i.






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章