示例代碼托管在: "http://www.github.com/dashnowords/blogs" 博客園地址: "《大史住在大前端》原創博文目錄" 華為雲社區地址: "【你要的前端打怪升級指南】" [TOC] 一. 題目 改造下麵的代碼,使之輸出0 9,寫出你能想到的所有解法。 首先作為前端開發 ...
目錄
示例代碼托管在:http://www.github.com/dashnowords/blogs
博客園地址:《大史住在大前端》原創博文目錄
華為雲社區地址:【你要的前端打怪升級指南】
一. 題目
改造下麵的代碼,使之輸出0 - 9,寫出你能想到的所有解法。
首先作為前端開發者,你起碼得知道下麵的代碼會輸出什麼,強烈建議自己動手試試能寫出多少種解法。
for (var i = 0; i< 10; i++){
setTimeout(() => {
console.log(i);
}, 1000)
}
二. 解法風暴
console.log(i)
在執行時,會按照詞法作用域來取得迴圈條件中的變數 i
的值,本題的基本思路實際上就是如何在console.log語句
和for迴圈條件
之間添加(或修改)代碼來隔離變數 i
的詞法作用域。
解法一:最容易想到的方法——ES6塊級作用域
//最容易想到的就是使用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;