Js 一道閉包問題
問題描述
前幾天去找前端的實習生工作,被一道 js 的閉包題目給卡住了,題目大致如下:
for (var i = 0; i < 10; i++) {
setTimeout(function() {
console.log(i);
}, 0);
}
稍微瞭解 js 的非同步機制的都知道,輸出結果是:
10
10
10
...
10
但是面試官又問我:其實希望得到的是0 1 2 ... 9
,如何能夠解決這個問題?我回答不上來。
思考分析
我一直從非同步的角度在思考,最終的結論也就是:這裡可以不用非同步函式……
然而這是個閉包問題,面試官給我的答案是使用立即執行函式(IIFE)。
我一下子明白過來,在《JavaScript高階程式設計》裡就有收錄這個問題。(Update: 第三版 7.2.1)
我發現,其實這個問題除了和閉包有關係之外,也和var
變數與其他語言(比如java)不同的作用域有關係。
通過手動的方式寫這個迴圈可以發現這些問題:
{
var i = 0;
setTimeout(function() { console.log(i); }, 0);
}
{
var i = 1;
setTimeout(function() { console.log(i); }, 0);
}
...
{
var i = 9;
setTimeout(function() { console.log(i); }, 0);
}
{
var i = 10;
// Update : i 要到達 10 才會不滿足語塊的執行條件,之前沒有注意,誤導了大家,不好意思
}
所以for
執行的每一步都是給i
重新賦值和往事件佇列推個事件。
由於for
的語塊不是var
變數的作用域範圍,在事件開始執行時,所有事件的回撥函式通過閉包拿到的i
是全域性作用域下的同一個i
。
解決方法
1. 使用立即執行函式
for (var i=0; i < 10; i++) {
(function (temp) {
setTimeout(function() {
console.log(temp);
}, 0);
})(i);
}
通過立即執行函式,回撥函式閉包獲得的不是原來的i
,而是立即執行函式的引數,這個引數剛好是i
的拷貝,閉包獲得的拷貝並不指向記憶體中的同一物件,程式碼執行起來大概是這個樣子:
{
var i = 0;
var temp0 = i;
setTimeout(function() { console.log(temp0); }, 0);
}
{
var i = 1;
var temp1 = i;
setTimeout(function() { console.log(temp1); }, 0);
}
...
{
var i = 9;
var temp9 = i;
setTimeout(function() { console.log(temp9); }, 0);
}
2. 使用 ES6 的 let 識別符號
for (let i = 0; i < 10; i++ ) {
setTimeout(function() {
console.log(i);
}, 0);
}
是的,使用let
的話就不會有這個閉包問題。
為什麼呢?
原來,在ES6中,為了修正var
奇怪的函式作用域,新增了let
,它的作用域是語塊:
{
let a = true;
}
console.log(a); // undefined
現在再想象一下迴圈:
{
let i = 0;
setTimeout(function() { console.log(i); }, 0);
}
{
let i = 1;
setTimeout(function() { console.log(i); }, 0);
}
...
{
let i = 9;
setTimeout(function() { console.log(i); }, 0);
}
回撥函式閉包獲取的i
不再是同一個了!
相關文章
- 閉包問題
- 閉包 | 淺談JavaScript閉包問題JavaScript
- 揹包問題的一道經典問題
- 經典 JS 閉包面試題JS面試題
- go 閉包捕獲問題Go
- JS閉包ClosureJS
- JS 事件迴圈,閉包,作用域鏈題JS事件
- js閉包的理解JS
- js中的閉包JS
- 淺談js閉包JS
- js函式閉包JS函式
- js閉包及閉包的經典使用場景JS
- JS進擊之路:閉包JS
- 深入理解JS閉包JS
- 對JS閉包的理解JS
- JS中的 閉包(Closure)JS
- JS作用域與閉包JS
- JS閉包作用域解析JS
- [JS]什麼是閉包?JS
- Python閉包區域性變數問題Python變數
- react要避免閉包問題,具體指的是哪些?React
- js閉包實現排他思想JS
- js閉包簡單總結JS
- JS-閉包(closure)的理解JS
- JS閉包的簡易使用JS
- [譯]理解JS中的閉包JS
- 【理解】一道 JS 面試題JS面試題
- vue中methods中的方法閉包快取問題Vue快取
- 從閉包引出來的一系列問題
- go語言採坑:閉包共享變數問題Go變數
- 從這兩道題重新理解,JS的this、作用域、閉包、物件JS物件
- 【JS基礎】作用域和閉包JS
- 淺談JS作用域、this及閉包JS
- 為什麼js會有閉包JS
- js 閉包 基礎 示例 高階JS
- 深入學習js之——閉包#8JS
- [JS]閉包和詞法環境JS
- [譯]JS閉包:For迴圈中的setTimeoutJS
- js--閉包與垃圾回收機制JS