示例代碼託管在:http://www.github.com/dashnowords/blogsjavascript
博客園地址:《大史住在大前端》原創博文目錄html
華爲雲社區地址:【你要的前端打怪升級指南】前端
[TOC]java
改造下面的代碼,使之輸出0 - 9,寫出你能想到的全部解法。node
首先做爲前端開發者,你起碼得知道下面的代碼會輸出什麼,強烈建議本身動手試試能寫出多少種解法。git
for (var i = 0; i< 10; i++){ setTimeout(() => { console.log(i); }, 1000) }
console.log(i)
在執行時,會按照詞法做用域來取得循環條件中的變量 i
的值,本題的基本思路實際上就是如何在console.log語句
和for循環條件
之間添加(或修改)代碼來隔離變量 i
的詞法做用域。github
解法一:最容易想到的方法——ES6塊級做用域promise
//最容易想到的就是使用let實現的局部做用域 for (let i = 0; i< 10; i++){ setTimeout(() => { console.log(i); }, 1000) } //變式 for (var i = 0; i< 10; i++){ let a = i; setTimeout(() => { console.log(a); }, 1000) }
解法二:大多數前端曾接觸過的第一種方法——IIFE(當即執行函數)瀏覽器
for(var i = 0; i < 10; i++){ (function(i){ setTimeout(() => { console.log(i); },1000) })(i); }
解法三:比較優雅的作法——setTimeout能夠接收多個參數函數
//setTimeout的函數簽名是setTimeout(fn, delay, ...params),params會做爲fn執行時的實參傳入 for (var i = 0; i< 10; i++){ setTimeout((i) => { console.log(i); }, 1000, i); }
解法四:利用函數方法bind爲setTimeout傳入預設參數
/*Function.prototype.bind(thisArg, ...args) * 會獲得一個新函數,新函數執行時預先設置了this和一部分參數,至關於把setTimeout改形成了偏函數 * bind執行後,setTimeout的第一個參數仍然是一個函數。 */ for (var i = 0; i < 10; i++){ setTimeout(((i) => { console.log(i); }).bind(null,i), 1000); }
解法五:利用禁術with
with
的做用是延長做用域鏈會在詞法做用域末端繼續添加參數定義,在正式開發中一般是禁用的。下圖右側的scope
一欄中就能夠看到local
做用域之上又多了一個with
引入的做用域,其中就包含傳入的i
值。
for(var i = 0; i < 10; i++){ with({i}){ setTimeout(() => { console.log(i); },1000) } }
解法六:利用Promise傳遞決議結果來隔離做用域
//在每一輪循環中的i做爲實參傳遞給promise的onFinished函數實現做用域隔離 for(var i = 0; i < 10; i++){ new Promise((resolve,reject)=>{ resolve(i); }).then((i)=>{ setTimeout(() => { console.log(i); },1000) }).catch(err=>{ console.log(err); }) }
解法七:利用try...catch來隔離做用域
for(var i = 0; i < 10; i++){ try{ throw i; }catch(i){ setTimeout(() => { console.log(i); },1000) } }
解法八:瀏覽器環境下setTimeout第一個參數能夠爲undefined(node.js中會報錯)
//console.log至關於同步運行,跟setTimeout實際沒什麼關係了 for (var i = 0; i< 10; i++){ setTimeout( console.log(i) , 1000) }
解法九:篡改console.log
let result = []; let consoleLog = console.log; console.log = (n)=> { result.push(n); if(result.length === 10) result.map((i,id)=>consoleLog(id)); } for(var i = 0; i < 10; i++){ setTimeout(() => { console.log(i); },1000) } //變式——稍微有點欠扁 console.log = (function(){ let consoleLog = console.log; let i = 0; return n => i++ === 9 && consoleLog('0,1,2,3,4,5,6,7,8,9'); })(); for(var i = 0; i < 10; i++){ setTimeout(() => { console.log(i); },1000) }
解法十:不按套路出牌的騷操做
for (var i = 0; i < 10; i++){ setTimeout(() => { console.log(i++ % 10); }, 1000); } //變式 for (var i = 0; i < 10; i++){ setTimeout(() => { console.log(i++); }, 1000); } i = 0;