关于一道JS面试题的思考
题目:
for (var i = ; i < ; i++) {
    setTimeout(function() {
        console.log(new Date, i);
    }, );
}
console.log(new Date, i);
1、面对这段代码时给出的结果也不尽相同,以下是典型的答案:
  A. 20% 的人会快速扫描代码,然后给出结果:0,1,2,3,4,5;
  B. 30% 的人会拿着代码逐行看,然后给出结果:5,0,1,2,3,4;
  C. 50% 的人会拿着代码仔细琢磨,然后给出结果:5,5,5,5,5,5;
只要你对 JS 中同步和异步代码的区别、变量作用域、闭包等概念有正确的理解,就知道正确答案是 C,代码的实际输出是:

2、如果我们约定,用箭头表示其前后的两次输出之间有 1 秒的时间间隔,而逗号表示其前后的两次输出之间的时间间隔可以忽略,代码实际运行的结果该如何描述?会有下面两种答案:
  A. 60% 的人会描述为:5 -> 5 -> 5 -> 5 -> 5,即每个 5 之间都有 1 秒的时间间隔;
  B. 40% 的人会描述为:5 -> 5,5,5,5,5,即第 1 个 5 直接输出,1 秒之后,输出 5 个 5;
这就要求候选人对 JS 中的定时器的工作机制非常熟悉,循环执行过程中,几乎同时设置了 5 个定时器,一般情况下,这些定时器都会在 1 秒之后触发,而循环完的输出是立即执行的,显而易见,正确的描述是 B。
3、追问:闭包
如果这道题仅仅是考察候选人对 JS 异步代码、变量作用域的理解,局限性未免太大,接下来如果期望代码的输出变成:5 -> 0,1,2,3,4,该怎么改造代码?熟悉闭包的同学很快能给出下面的解决办法:
for (var i = ; i < ; i++) {
    (function(j){
        setTimeout(function() {
            console.log(new Date, j);
        }, );
    })(i);
}
console.log(new Date, i);
巧妙的利用 IIFE(Immediately Invoked Function Expression:声明即执行的函数表达式)来解决闭包造成的问题,确实是不错的思路,但是初学者可能并不觉得这样的代码很好懂,至少笔者初入门的时候这里琢磨了一会儿才真正理解。
有没有更符合直觉的做法?答案是有,我们只需要对循环体稍做手脚,让负责输出的那段代码能拿到每次循环的 i 值即可。该怎么做呢?利用 JS 中基本类型(Primitive Type)的参数传递是按值传递(Pass by Value)的特征,不难改造出下面的代码:
var output = function(i){
    setTimeout(function() {
        console.log(new Date, i);
    }, );
}
for (var i = ; i < ; i++) {
    output(i);//值传递传个i值过去
}
console.log(new Date, i);
4、追问
如果期望代码的输出变成 0 -> 1 -> 2 -> 3 -> 4 -> 5,并且要求原有的代码块中的循环和两处 console.log 不变,该怎么改造代码?新的需求可以精确的描述为:代码执行时,立即输出 0,之后每隔 1 秒依次输出 1,2,3,4,循环结束后在大概第 5 秒的时候输出 5(这里使用大概,是为了避免钻牛角尖的同学陷进去,因为 JS 中的定时器触发时机有可能是不确定的)
for (var i = ; i < ; i++) {
     (function(j) {
          setTimeout(function() {
           console.log(new Date, j);
          },  * j); // 这里修改 0~4 的定时器时间
     })(i);
}
setTimeout(function() { // 这里增加定时器,超时设置为 5 秒
 console.log(new Date, i);
},  * i);

不得不承认,这种做法虽粗暴有效,但是不算是能额外加分的方案。如果把这次的需求抽象为:在系列异步操作完成(每次循环都产生了 1 个异步操作)之后,再做其他的事情,代码该怎么组织?聪明的你是不是想起了什么?对,就是 Promise。
ES6、ES7的解决方案(后续)。
关于一道JS面试题的思考的更多相关文章
- new与属性访问的顺序,从一道JS面试题说起
		这段时间一直在研究设计模式,在看工厂模式的时候,看到一段代码 VehicleFactory.prototype.createVehicle = function ( options ) { if( o ... 
- 一道JS面试题引发的血案
		刚入职新公司,属于公司萌新一枚,一天下午对着屏幕看代码架构时. BI项目组长给我看了一道面试别人的JS面试题. 虽然答对了,但把理由说错了,照样不及格. 话不多说,直接上题: var a = 1; s ... 
- 一道JS面试题所引发的"血案",透过现象寻本质,再从本质看现象
		觉得本人写的不算很烂的话,可以登录关注一下我的GitHub博客,新手写东西写的不好之处,还望见谅,毕竟水平有限,写东西只为交流提高,一起学习,还望大神多加指点,指出纰漏,和提出宝贵的意见,博客会坚持写 ... 
- 一道js面试题看变量的作用域
		[问题]分别求下面程序的输出结果: 1. <script type="text/javascript"> var a = 10; sayHi(); function s ... 
- 一道js面试题
		当然这道面试题并不一定就能在你面试的时候遇到,但是不怕一万就怕万一,会的多一些还是好的. 问:怎么判断一串字符中哪个字符出现的最多,最多几次或者这串字符分别有哪些,每个字符出现了几次.写你请出运算代 ... 
- 腾讯的一道js面试题(原型)
		有一只小狗叫花花,它会“汪汪”叫,他的同伴也会汪汪叫,后来环境发生了变化,新出生的狗不会再“汪汪”叫,而变成“呜呜”叫. 试通过继承来达到目的 function Dog(){ 2 this.bark ... 
- 一道经典JS面试题
		超过80%的候选人对下面这道JS面试题的回答情况连及格都达不到.这究竟是怎样神奇的一道JS面试题?他考察了候选人的哪些能力?对正在读本文的你有什么启示? 不起眼的开始 招聘前端工程师,尤其是中高级前端 ... 
- 一道经典的js面试题
		# 声明:学习编程语言最好的方式就是通过实例学习 ## 下面是我在博客上看到的一道js面试题,可以说非常经典,下面会以最简单的方式让你理解题目:```bashfunction Foo() { getN ... 
- 那晚征服的一道js经典的面试题
		今天朋友共享了一道js中经典的面试题,需求是这样的 给定你任意一个字符串,让你写出一个算法,求算出该字符串中出现次数最多的一个字符,并将其结果输出 刚拿到这道题的第一感觉便是定义一个count计时器, ... 
随机推荐
- [ 原创 ]学习笔记-三种向ListView中填充简单文本的方法
			Android 中ListView是很重要的一块内容 掌握ListView的基本用法 对学习安卓起着举足轻重的作用 今天就介绍一下三种向ListView 填充简单文本的方法 填充其他数据类型的用法之后 ... 
- HDU 4348 To the moon 主席树 在线更新
			http://acm.hdu.edu.cn/showproblem.php?pid=4348 以前做的主席树没有做过在线修改的题做一下(主席树这种东西正经用法难道不是在线修改吗),标记永久化比较方便. ... 
- 方程式0day图形化利用工具
			最近方程式的漏洞着实活了一把,分析了下githup上面的文件目录,找到了利用文件,主要是针对windows主机的SMB.RDP协议进行攻击,因为我主要根据他们提供的payload的程序,利用这两个模块 ... 
- django: ListView解读
			[转载注明出处: http://www.cnblogs.com/yukityan/p/8039041.html ] django内置列表视图: # 导入 from django.views.gener ... 
- ACdream 速攻组~
			1007 a + b /*这题就是一个快速幂,但是十分猥琐的是,模是1e10 + 7,不是1e9 + 7,这就产生了一个爆long long的问题.所以要对快速幂中的乘法操作进行一下改造.请教了BIT ... 
- redis的搜索组件 redis-search4j
			redis-search4j是一款基于redis的搜索组件. 特点 1.基于redis,性能高效 2.实时更新索引 3.支持Suggest前缀.拼音查找(AutoComplete功能) 4.支持单个或 ... 
- Asky极简教程:零基础1小时学编程,已更新前8节
			Asky极简架构 开源Asky极简架构.超轻量级.高并发.水平扩展.微服务架构 <Asky极简教程:零基础1小时学编程>开源教程 零基础入门,从零开始全程演示,如何开发一个大型互联网系统, ... 
- offset大家族(一)
			<!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ... 
- [JAVA]  JAVA JDK 安装配置
			JDK 安装 下载安装 下载JDK 从oracle官方网站下载并安装JDK. 下载使用文档 从oracle官方网站下载使用帮助文档. 安装库源文件 源文件位于安装目录的 /Library/Java/J ... 
- PostgreSQL SystemTap on Linux 转
			PostgreSQL 支持动态跟踪, 可以通过dtrace或者systemtap工具统计相关探针的信息. 安装systemtap yum install systemtap kernel-debugi ... 
