版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
【摘要】 开脑洞,也巩固基础知识
示例代码托管在:http://www.github.com/dashnowords/blogs
博客园地址:《大史住在大前端》原创博文目录
一. 题目
改造下面的代码,使之输出0 - 9,写出你能想到的所有解法。
首先作为前端开发者,你起码得知道下面的代码会输出什么,强烈建议自己动手试试能写出多少种解法。
1 for (var i = 0; i< 10; i++){ 2 setTimeout(() => { 3 console.log(i); 4 }, 1000) 5 }
二. 解法风暴
console.log(i)
在执行时,会按照词法作用域来取得循环条件中的变量 i
的值,本题的基本思路实际上就是如何在console.log语句
和for循环条件
之间添加(或修改)代码来隔离变量 i
的词法作用域。
解法一:最容易想到的方法——ES6块级作用域
1 //最容易想到的就是使用let实现的局部作用域 2 for (let i = 0; i< 10; i++){ 3 setTimeout(() => { 4 console.log(i); 5 }, 1000) 6 } 7 //变式 8 for (var i = 0; i< 10; i++){ 9 let a = i; 10 setTimeout(() => { 11 console.log(a); 12 }, 1000) 13 }
解法二:大多数前端曾接触过的第一种方法——IIFE(立即执行函数)
1 for(var i = 0; i < 10; i++){ 2 (function(i){ 3 setTimeout(() => { 4 console.log(i); 5 },1000) 6 })(i); 7 }
解法三:比较优雅的做法——setTimeout可以接收多个参数
1 //setTimeout的函数签名是setTimeout(fn, delay, ...params),params会作为fn执行时的实参传入 2 for (var i = 0; i< 10; i++){ 3 setTimeout((i) => { 4 console.log(i); 5 }, 1000, i); 6 }
解法四:利用函数方法bind为setTimeout传入预设参数
1 /*Function.prototype.bind(thisArg, ...args) 2 * 会得到一个新函数,新函数执行时预先设置了this和一部分参数,相当于把setTimeout改造成了偏函数 3 * bind执行后,setTimeout的第一个参数仍然是一个函数。 4 */ 5 for (var i = 0; i < 10; i++){ 6 setTimeout(((i) => { 7 console.log(i); 8 }).bind(null,i), 1000); 9 }
解法五:利用禁术with
with
的作用是延长作用域链会在词法作用域末端继续添加参数定义,在正式开发中通常是禁用的。下图右侧的scope
一栏中就可以看到local
作用域之上又多了一个with
引入的作用域,其中就包含传入的i
值。
1 for(var i = 0; i < 10; i++){ 2 with({i}){ 3 setTimeout(() => { console.log(i); },1000) 4 } 5 }
解法六:利用Promise传递决议结果来隔离作用域
1 //在每一轮循环中的i作为实参传递给promise的onFinished函数实现作用域隔离 2 for(var i = 0; i < 10; i++){ 3 new Promise((resolve,reject)=>{ 4 resolve(i); 5 }).then((i)=>{ 6 setTimeout(() => { console.log(i); },1000) 7 }).catch(err=>{ 8 console.log(err); 9 }) 10 }
解法七:利用try...catch来隔离作用域
1 for(var i = 0; i < 10; i++){ 2 try{ 3 throw i; 4 }catch(i){ 5 setTimeout(() => { console.log(i); },1000) 6 } 7 }
解法八:浏览器环境下setTimeout第一个参数可以为undefined(node.js中会报错)
1 //console.log相当于同步运行,跟setTimeout实际没什么关系了 2 for (var i = 0; i< 10; i++){ 3 setTimeout( 4 console.log(i) 5 , 1000) 6 }
解法九:篡改console.log
1 let result = []; 2 let consoleLog = console.log; 3 console.log = (n)=> { 4 result.push(n); 5 if(result.length === 10) result.map((i,id)=>consoleLog(id)); 6 } 7 8 for(var i = 0; i < 10; i++){ 9 setTimeout(() => { 10 console.log(i); 11 },1000) 12 } 13 14 //变式——稍微有点欠扁 15 console.log = (function(){ 16 let consoleLog = console.log; 17 let i = 0; 18 return n => i++ === 9 && consoleLog('0,1,2,3,4,5,6,7,8,9'); 19 })(); 20 21 for(var i = 0; i < 10; i++){ 22 setTimeout(() => { 23 console.log(i); 24 },1000) 25 }
解法十:不按套路出牌的骚操作
1 for (var i = 0; i < 10; i++){ 2 setTimeout(() => { 3 console.log(i++ % 10); 4 }, 1000); 5 } 6 7 //变式 8 for (var i = 0; i < 10; i++){ 9 setTimeout(() => { 10 console.log(i++); 11 }, 1000); 12 } 13 i = 0;
作者:大史不说话
HDC.Cloud 华为开发者大会2020 即将于2020年2月11日-12日在深圳举办,是一线开发者学习实践鲲鹏通用计算、昇腾AI计算、数据库、区块链、云原生、5G等ICT开放能力的最佳舞台。