招聘前端工程师,尤其是中高级前端工程师,扎实的 JS 基础绝对是必要条件,基础不扎实的工程师在面对前端开发中的各种问题时大概率会束手无策。

如果你是一名前端开发,或者对前端感兴趣,可以看看这道题:

for (var i = 0; i < 5; i++) {
   setTimeout(function() {
       console.log(new Date, i);
   }, 1000);
}
console.log(new Date, i);

请分析它实际运行的结果。

  • A. 20% 的人会快速扫描代码,然后给出结果:0,1,2,3,4,5;

  • B. 30% 的人会拿着代码逐行看,然后给出结果:5,0,1,2,3,4;

以上是常见的两种回答,只要你对 JS 中同步和异步代码的区别、变量作用域、闭包等概念有正确的理解,就知道代码的实际输出是:

2017-03-18T00:43:45.873Z 5

2017-03-18T00:43:46.866Z 5

2017-03-18T00:43:46.868Z 5

2017-03-18T00:43:46.868Z 5

2017-03-18T00:43:46.868Z 5

2017-03-18T00:43:46.868Z 5

追问 1:输出速度

如果我们约定,用箭头表示其前后的两次输出之间有 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。

追问 2:闭包

如果这道题仅仅是考察候选人对 JS 异步代码、变量作用域的理解,局限性未免太大,接下来我会追问,如果期望代码的输出变成:5 -> 0,1,2,3,4,该怎么改造代码?熟悉闭包的同学很快能给出下面的解决办法:

for (var i = 0; i < 5; i++) {
   (function(j) {  // j = i
       setTimeout(function() {
           console.log(new Date, j);
       }, 1000);
   })(i);
}

console.log(new Date, i);
var output = function (i) {
   setTimeout(function() {
       console.log(new Date, i);
   }, 1000);

};

for (var i = 0; i < 5; i++) {
   output(i);  // 这里传过去的 i 值被复制了
}

console.log(new Date, i);

能给出上述 2 种解决方案的候选人可以认为对 JS 基础的理解和运用是不错的,可以各加 10 分。

标签: function,工程师,开发