这篇文章不是关于最好的JavaScript库、常见的开发实践或任何新的ES6功能。相反,在讨论JavaScript时,面试中通常会提到三件事。我自己也被问过这些问题,我的朋友也告诉我他们也被问过。当然,这些并不是您在面试前应该学习的唯一三件事-您可以通过多种方式更好地为即将到来的面试做准备-但面试官可能会问以下三个问题来判断您对JavaScript语言的理解以及你对DOM的掌握。开始吧!请注意,我们将在下面的示例中使用vanillaJavaScript,因为面试官通常想知道您在没有jQuery等库帮助的情况下对JavaScript和DOM的理解程度。问题1:事件委托在构建应用程序时,有时需要将事件绑定到页面上的按钮、文本或图像,以便在用户与元素交互时执行某些操作。如果我们以一个简单的待办事项列表为例,面试官可能会告诉你,当用户点击列表中的某个项目时,执行一些操作。他们希望您在JavaScript中实现此功能,假设有以下HTML代码:WalkthedogPaybillsMakedinnerCodeforonehour您可能希望执行以下操作以将事件绑定到元素:document.addEventListener('DOMContentLoaded',function(){letapp=document.getElementById('todo-app');letitimes=app.getElementsByClassName('item');for(letitemofitems){item.addEventListener('click',function(){alert('youclickedonitem:'+item.innerHTML);})}})虽然这在技术上可行,但问题是将事件分别绑定到每个项目。现在这对于4个元素来说没问题,但是如果将10,000个项目添加到待办事项列表中(他们可能有很多事情要做)怎么办?该函数随后会创建10,000个独立的事件监听器,并将每个事件监听器绑定到DOM,这种代码执行效率非常低。面试的时候***先问面试官用户可以输入多少个***元素。例如,如果不超过10,上面的代码就可以正常工作。但是如果对用户可以输入的条目数没有限制,那么你应该使用更高效的解决方案。如果您的应用程序最终可能有数百个事件侦听器,则更有效的解决方案是将一个事件侦听器实际绑定到整个容器,然后能够在单击时访问每个列表项,这称为事件委托,它是比附加单独的事件处理程序更有效。下面是事件委托的代码:document.addEventListener('DOMContentLoaded',function(){letapp=document.getElementById('todo-app');app.addEventListener('click',function(e){if(e.target&&e.target.nodeName==='LI'){letitem=e.target;alert('youcickedonitem:'+item.innerHTML)}})})问题二:在循环中使用闭包闭包经常出现在面试中,所以面试官可以衡量你对JS的熟悉程度以及你是否知道何时使用闭包。闭包基本上是内部函数,可以访问其范围之外的变量。闭包可用于实现隐私和创建函数工厂。一个关于闭包的常见面试问题如下:编写一个函数,它将迭代一个整数列表,并在延迟3秒后打印每个元素的索引。通常不正确的写法是:constarr=[10,12,15,21];for(vari=0;i=interval){//如果timeinterval如果大于我们设置的时间间隔阈值,则执行回调last=now;fn.apply(context,args);}}}//使用throttle包裹滚动回调constbetter_scroll=throttle(()=>console.log('触发滚动事件'),1000)document.addEventListener('scroll',better_scroll)Debounce:***选手说了算,防抖的主要思路是:我来等你到最后。在一定时间内,无论你触发了多少次回调?我只认得一次***。继续大胃王比赛的故事,这次换了一种不一样的比赛方式,不限时间,吃到吃不下为止。当每位参赛者吃完食物时,如果接下来的10分钟内没有人进食,则比赛结束。如果有人在10分钟内仍然可以进食,则游戏将继续进行,直到下一次10分钟内没有人进食。对比throttle理解debounce:在throttle的逻辑中,“裁判”说了算,当比赛时间到了,就会执行回调函数。而debounce认为最后的选手说了算,只要还有食物吃,就会重新设置一个新的timer。现在一起实现一个debounce://fn是我们需要包装的事件回调,delay是每个延迟执行函数的等待时间debounce(fn,delay){//Timerlettimer=null//将debounce处理结果作为一个返回functionreturnfunction(){//调用时保持this上下文letcontext=this//调用时保持传入的参数letargs=arguments//每次触发事件时,清除if(timer){clearTimeout(timer)之前的老定时器}//设置一个新的定时器timer=setTimeout(function(){fn.apply(context,args)},delay)}}//使用debounce来包装滚动回调constbetter_scroll=debounce(()=>console.log('scrolleventtriggered'),1000)document.addEventListener('scroll',better_scroll)使用Throttle优化Debouncedebounce的问题在于它“太有耐心”。试想一下,如果用户的操作非常频繁——他并没有等到debounce设置的延迟时间结束再进行下一个操作,那么debounce每次都为用户重新生成定时器,回调函数就被延迟了无数次。频繁的延迟会导致用户长时间得不到响应,用户也会产生“这个页面卡住了”的印象。为了避免弄巧成拙,我们需要用throttle的思想来制造一个“底线”debounce——你可以等,但我有我的原则:在延迟时间内,我可以为你重新生成定时器;但是只要延迟到时候,我就得给用户一个响应。这种“整合”throttle和debounce的思路已经被很多成熟的前端库应用到他们增强版throttle函数的实现中://fn是我们需要包装的事件回调,delay是threshold的时间间隔functionthrottle(fn,delay){//last为上次触发回调时间,timer为定时器letlast=0,timer=null//将throttle处理结果作为函数返回returnfunction(){//保留thiscontextwhencallingletcontext=this//保留调用时传入的参数letargs=arguments//记录本次触发回调的时间letnow=+newDate()//判断上次触发时间与本次是否有时间差triggertimeislessthanthethresholdofthetimeintervalif(now-lastconsole.log('scrolleventtriggered'),1000)document.addEventListener('scroll',better_scroll)