当前位置: 首页 > 科技观察

80%申请者都过不了的JS面试题

时间:2023-03-16 14:28:34 科技观察

写在最前面。笔者在过去的2年里作为面试官面试过上百位前端工程师。我惊奇地发现,80%以上的考生对以下问题的回答连及格线都没有。这是什么神奇的面试题?他考察应聘者的哪些能力?对阅读本文的您有何启示?让我慢慢开始招聘前端工程师,尤其是中高级前端工程师,扎实的JS基础绝对是必备条件。没有扎实基础的工程师在前端开发中面对各种问题时,多半会束手无策。在考察考生的JS基础时,我经常会提供如下一段代码,然后让考生分析其实际运行的结果:for(vari=0;i<5;i++){setTimeout(function(){console.log(newDate,i);},1000);}console.log(newDate,i);这段代码很短,只有7行,我想能看懂这里的同学应该不需要我逐行解释What是代码在做什么。考生在面对这个代码时给出了不同的结果,以下是典型的答案: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,代码实际输出为:2017-03-18T00:43:45.873Z52017-03-18T00:43:46.866Z52017-03-18T00:43:46.868Z52017-03-18T00:43:46.868Z52017-03-18T00:43:46.8178T002下一个,I48:48:45会跟进:如果我们同意用箭头表示前后两个输出之间有1秒的间隔,用逗号表示前后两个输出之间的间隔可以忽略,如何描述代码的实际运行结果?会有以下两种答案:A.60%的人会描述为:5->5->5->5->5,即每个5之间有1秒B.40%的人会描述为:5->5,5,5,5,5,即直接输出第一个5,1秒后输出5个5;这就需要考生对JS中的定时器工作机制非常熟悉。在循环执行过程中,几乎同时设置了5个定时器。一般这些定时器会在1秒后被触发,并立即执行循环后的输出。显然,正确的描述是B。如果这里算pass的话,参加面试的100人中只有20人能??pass。看到这里的同学可以仔细想想,你通过了吗?追问一:闭包如果这道题只是考考考生对JS异步代码的理解,对变量作用域的理解就太局限了。下面请教一下,如果预期的代码输出变成:5->0,1,2,3,4,如何修改代码?熟悉闭包的同学都非常熟悉。赶紧给出如下解决方案:for(vari=0;i<5;i++){(function(j){//j=isetTimeout(function(){console.log(newDate,j);},1000);})(i);}console.log(newDate,i);使用IIFE(ImmediatelyInvokedFunctionExpression:声明被执行的函数表达式)来解决闭包带来的问题确实是个好主意。但是初学者可能不会觉得这样的代码好理解,至少我刚开始的时候想了一会儿才真正理解。有没有更直观的方法?答案是肯定的,我们只需要对循环体做一点操作,让负责输出的代码就可以得到每个循环的i值。怎么做?利用JS中原始类型(PrimitiveType)中按值传递(PassbyValue)的特性,不难改造出如下代码:varoutput=function(i){setTimeout(function(){console.log(newDate,i);},1000);};for(vari=0;i<5;i++){output(i);//这里传递的i的值被复制}console.log(newDate,i);能给出以上两种解决方案的考生,可以认为自己对JS的基础知识有了很好的理解和应用,可以各加10分。当然,在实际面试中,也有应聘者给出了如下代码:for(leti=0;i<5;i++){setTimeout(function(){console.log(newDate,i);},1000);}console.日志(新日期,我);细心的同学会发现,这里只有一个很细微的变化,就是在ES6中使用了块作用域(BlockScope),而不是var,但是代码在实际运行的时候会报错,因为***i该输出使用的不在其范围内,我只存在于循环内。能想到ES6特性的同学没有答对,但是表现出了对ES6的理解,可以加5分继续后面的问题。追问2:有ES6经验的前端同学看完可能有点不耐烦了。说了这么多,都是他知道的了。别担心,挑战的难度会不断增加。然后继续问上面的问题:如果期望代码的输出变成0->1->2->3->4->5,而原代码块中的循环和两个console.log是要求保持不变,如何修改代码?新的需求可以准确描述为:当代码执行时,立即输出0,然后每隔1秒输出1、2、3、4,循环结束后大约第5秒输出5(这里是粗略的使用避免被卡在墙角的同学,因为JS中定时器的触发时机可能是不确定的,具体可以参考HowJavascriptTimersWork)。看到这里,有同学会给出如下可行方案:for(vari=0;i<5;i++){(function(j){setTimeout(function(){console.log(newDate,j);},1000*j));//这里修改0~4的定时器时间})(i);}setTimeout(function(){//这里添加一个定时器,设置超时时间为5秒console.log(newDate,i);},1000*i);不得不承认,虽然这种做法很残酷,也很有效,但并不是一个可以加分的方案。如果这次的需求抽象为:一系列异步操作完成后(每个周期产生一个异步操作),再做其他事情,代码应该如何组织?你有什么聪明的想法吗?是的,它是Promise。可能有同学会问,不就是在控制台输出几个数字吗?至于用大锤杀鸡?你要知道,面试官真正想考察的是应聘者是否具备一定的能力和素质,因为在现代前端开发中,异步代码随处可见。熟悉并掌握异步操作的流程控制是成为一名合格开发人员的基本技能。往下走,不难给出一个基于Promise的解决方案(既然Promise是ES6的新特性,那我们新的代码写成ES6是不是更好?这样写的话,大概率是theinterviewerwillbesurprisedGoodimpression):consttasks=[];for(vari=0;i<5;i++){//这里i的声明不能改成let,想改怎么办?((j)=>{tasks.push(newPromise((resolve)=>{setTimeout(()=>{console.log(newDate,j);resolve();//resolve一定要在这里,否则代码会notWorkasexpected},1000*j);//定时器的超时时间逐渐增加}));})(i);}Promise.all(tasks).then(()=>{setTimeout(()=>{console.log(newDate,i);},1000);//注意只需要设置超时为1秒即可});相比之下,笔者更喜欢像下面这样更简洁的代码,你懂的编程风格也是很多面试官考察的重点。代码阅读的粒度更小,模块化更好,这无疑会是一个加分项。consttasks=[];//这里存放的是异步操作的Promiseconstoutput=(i)=>newPromise((resolve)=>{setTimeout(()=>{console.log(newDate,i);resolve();},1000*i);});//生成所有异步操作for(vari=0;i<5;i++){tasks.push(output(i));}//异步操作完成后,输出***iPromise.all(tasks).then(()=>{setTimeout(()=>{console.log(newDate,i);},1000);});读到这里,恭喜你,下次面试遇到类似问题,至少能拿到80分。我们都知道使用Promise处理异步代码比起回调机制让代码更具可读性,但是使用Promise的问题也很明显,就是如果不处理Promise的拒绝,就会将错误抛入黑洞.幸运的是,新版本的Chrome和Node7.x可以针对未处理的异常给出UnhandledRejectionWarning,而排查这些错误需要一些特殊的技能(浏览器、Node.js)。追问3:既然看到这里是ES7,那就再坚持2分钟,接下来的内容会让你明白自己的坚持是值得的。大多数面试官在决定聘用候选人之前,需要考察另一个重要的能力,那就是技术上的自我激励。说白了,应聘者好像被内在的马达驱动着,把工程领域的问题美美地解决了,不断跟着业务和技术变得越来越牛逼,牛逼是什么?推荐阅读这篇ProgramLife的分析。回到正题,既然已经拿下了Promise,那如何利用ES7中的asyncawait特性让这段代码更加简洁呢?你能根据你目前的知识给出答案吗?请在这里暂停一分钟并思考一下。下面是作者给出的参考代码://用其他语言模拟睡眠,其实可以是任意异步操作constsleep=(timeountMS)=>newPromise((resolve)=>{setTimeout(resolve,timeountMS);});(async()=>{//语句执行的异步函数表达式for(vari=0;i<5;i++){awaitsleep(1000);console.log(newDate,i);}awaitsleep(1000);console.log(newDate,i);})();总结感谢您花时间阅读本文。相信大家学到的不仅仅是使用JS精准控制代码输出的各种技巧,还有前端工程师的成长前景:扎实的语言基础、与时俱进的能力、过硬的技术能力自驾。