zoukankan      html  css  js  c++  java
  • javascript基础修炼(13)——记一道有趣的JS脑洞练习题【华为云技术分享】

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
    本文链接:https://blog.csdn.net/devcloud/article/details/102517623

    【摘要】 开脑洞,也巩固基础知识

    示例代码托管在: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开放能力的最佳舞台。

    欢迎报名参会

  • 相关阅读:
    Flask 的 请求扩展 与 中间件
    Flask的配置文件 与 session
    django中的FBV和CBV
    Flask开启多线程、多进程
    WPF获取原始控件样式
    一个40岁老码农的总结,奋斗没有意义,选择大于努力
    FastText 分析与实践
    Flask的多app应用,多线程如何体现
    Python 远程调用MetaSploit
    GitLab 7.5.3 CentOS7安装和SMTP配置
  • 原文地址:https://www.cnblogs.com/huaweicloud/p/11865787.html
Copyright © 2011-2022 走看看