JavaScript是所有现代浏览器的官方语言。同样的,JavaScript面试题出现在各种各样的面试中。

这篇文章不是讲述JavaScript最新的库、日常的开发实践,或是ES6的新功能。当然了,上面说的这3点经常出现在JavaScript的面试中。我自己也曾经问过面试者这些问题,我的朋友告诉我,他们同样也是。

当然,你去面试前不能只准备上面提到的3点,这里有许多方法让你能够更好的面对即将到来的面试。

但是,接下来的这3个问题,面试官可能会根据你的回答,去判断你对JavaScript和DOM的了解程度。

所以,现在开始吧!请注意,我们在接下来的例子中将使用原生JavaScript,因为你的面试官可能想看看在没有第三方库的情况下,比如jQuery,你对JavaScript和DOM的理解程度。

问题 #1: 事件委托

当构建一个程序时,有些时候你需要监听按钮、文本、图片的事件,因为当用户与界面元素发生互动时,你需要执行一些动作。

如果我们拿到一个简单的待办事项列表,就像下面例子中这样,面试官可能会告诉你,当用户点击其中一个列表项时,我们需要执行一些动作。面试官希望你用JavaScript实现这个功能,假设HTML代码如下:

<ul id="todo-app">
<li class="item">Walk the dog</li>
<li class="item">Pay bills</li>
<li class="item">Make dinner</li>
<li class="item">Code for one hour</li>
</ul>

你也许会像下面这样去给这些元素绑定事件监听:

document.addEventListener('DOMContentLoaded', function() {

  let app = document.getElementById('todo-app');
let items = app.getElementsByClassName('item'); // 给每个列表项绑定事件监听器
for (let item of items) {
item.addEventListener('click', function() {
alert('you clicked on item: ' + item.innerHTML);
});
} });

虽然实现了功能,但问题是我们给每个列表项都单独绑定了事件监听。现在只有4个元素,没问题,但是如果有10000个待办事项呢(程序可能还有其他事情要做)?接下来你会创建10000个事件监听函数绑定要每个DOM元素上,这是十分低效的。

在面试时,最好问问面试官用户可以输入的最大元素数量。如果数量小于10个,上面的例子可以很好的运行。但是如果数量不受限制,你可能需要使用一个更有效率的解决办法。

如果你的应用有数百个事件监听,更有效率的解决办法是:把事件监听绑定在包裹这些元素的容器上,当元素被点击时,我可以得到当前点击的确切元素。这种技巧叫事件委托,而且它比给每个元素绑定事件监听效率要高。

用事件委托的方式实现上面的功能:

document.addEventListener('DOMContentLoaded', function() {

  let app = document.getElementById('todo-app');

  // 把事件监听器绑定在它们的容器上
app.addEventListener('click', function(e) {
if (e.target && e.target.nodeName === 'LI') {
let item = e.target;
alert('you clicked on item: ' + item.innerHTML);
}
}); });

问题 #2: 在循环内使用闭包

闭包问题常常在面试中被提出,面试官能通过它估算出你对JavaScript的熟悉程度,同时了解你对闭包是否熟悉。

闭包的精髓就是:在外部函数中可以读取到内部函数的作用域。闭包可以做这些事:创建私有变量创建私有函数等。大多数关于闭包的面试题像这样:

循环一个数组,并在3秒后打印出每个数组元素的索引。

一个通常的实现方式像下面这样,但其实是错误的:

const arr = [10, 12, 15, 21];
for (var i = 0; i < arr.length; i++) {
setTimeout(function() {
console.log('The index of this number is: ' + i);
}, 3000);
}

如果运行上面代码你会发现,3秒后,每次循环输出的都是4,而不是期望的0,1,2,3

真正的去理解为什么会发生这些,它会帮助你更好的认识JavaScript,因为你也不知道面试官会出怎样确切的题测试你。

出现上面现象的原因是:setTimeout会创建一个函数(就是闭包),它可以读取到外部作用域,每个循环都包含了索引i。函数在3秒后执行,它打印出外部作用域中i的值,在循环结束后i等于4,因为它的循环周期经历了0,1,2,3,4,最终在i为4时停止。

这里有几种正确的方法去解决这个问题,下面列举两种:

const arr = [10, 12, 15, 21];
for (var i = 0; i < arr.length; i++) {
// 通过传递变量 i
// 在每个函数中都可以获取到正确的索引
setTimeout(function(i_local) {
return function() {
console.log('The index of this number is: ' + i_local);
}
}(i), 3000);
}
const arr = [10, 12, 15, 21];
for (let i = 0; i < arr.length; i++) {
// 使用ES6的let语法,它会创建一个新的绑定
// 每个方法都是被单独调用的
// 详情请移步至: http://exploringjs.com/es6/ch_variables.html#sec_let-const-loop-heads
setTimeout(function() {
console.log('The index of this number is: ' + i);
}, 3000);
}

问题 #3: 函数防抖(Debouncing)

有些浏览器事件可以在很短的时间内执行多次,就像改变浏览器窗口尺寸和滚动页面。如果你绑定一个事件去监听窗口的滚动,用户快速连续的滚动页面,这个事件可能会在3秒内被触发几千次。这可能会导致很严重的性能问题。

如果你们在面试中讨论构建一个应用,谈到类似滚动、改变窗口尺寸或键盘按下的事件时,一定会提及函数防抖、函数节流(Throttling)去优化页面速度和性能。一个真实的案例,来自guest post on css-tricks:

在2011年,一个问题在Twitter上被提出:当你滚动Twitter feed时,它会十分缓慢且迟钝。John Resig就这个问题发布了一篇博客,它解释了直接绑定函数到滚动事件上是多么糟糕和昂贵的事。

函数防抖是解决这个问题的一种方式,通过限制函数被调用的次数。一个正确实现函数防抖的方法是:把多个函数放在一个函数里调用,隔一定时间执行一次。这里有一个使用原生JavaScript实现的例子,用到了作用域、闭包、this和定时时间:

// debounce函数用来包裹我们的事件
function debounce(fn, delay) {
// 持久化一个timer
let timer = null;
// 闭包可以获取到timer
return function() {
// 通过函数获取到作用域和参数列表
// 通过 'this' 和 'arguments'
let context = this;
let args = arguments;
// 如果事件被触发,清除timer并重新开始计时
clearTimeout(timer);
timer = setTimeout(function() {
fn.apply(context, args);
}, delay);
}
}

当这个函数绑定在一个事件上,只有经过一段指定的时间后才会被调用。

你可以像这样去使用这个函数:

// 当用户滚动时函数会被调用
function foo() {
console.log('You are scrolling!');
} // 在事件触发的两秒后,我们包裹在debounce中的函数才会被触发
let elem = document.getElementById('container');
elem.addEventListener('scroll', debounce(foo, 2000));

函数节流是另一个类似函数防抖的技巧,除了使用等待一段时间再调用函数的方法,函数节流还限制固定时间内只能调用一次。所以一个事件如果在100毫秒内发生10次,函数节流会每2秒调用一次函数,而不是100毫秒内全部调用。

想了解更多关于函数防抖和函数节流的信息,下面的文章和教程可能会帮到你:

译者注:之前听朋友讲过这个例子,我就很清晰的分辨了两者的区别:

想象每天上班大厦底下的电梯。把电梯完成一次运送,类比为一次函数的执行和响应。假设电梯有两种运行策略 throttle 和 debounce ,超时设定为15秒,不考虑容量限制。

throttle 策略的电梯。保证如果电梯第一个人进来后,15秒后准时运送一次,不等待。如果没有人,则待机。

debounce 策略的电梯。如果电梯里有人进来,等待15秒。如果又人进来,15秒等待重新计时,直到15秒超时,开始运送。

本文由Rockjins Blog翻译,转载请与译者联系。否则将追究法律责任。

avaScript技术面试时要小心的三个问题的更多相关文章

  1. 面试时,当你有权提问时,别客气,这是个逆转的好机会(内容摘自Java Web轻量级开发面试教程)

    前些天,我在博客园里写了篇文章,如何在面试中介绍自己的项目经验,收获了2千多个点击,这无疑鼓舞了我继续分享的热情,今天我来分享另外一个面试中的甚至可以帮助大家逆转的技巧,本文来是从 java web轻 ...

  2. 十大面试问题解惑,秒杀一切HR、技术面试

    最能体现求职者能力的就是面试,能不能拿到Offer,取决于你面试时的表现,只有有准备才能在面试过程中游刃有余.小编收集了10个面试官最爱提的问题,虽然题目千变万化,但是万变不离其宗,只要掌握了答题的技 ...

  3. java技术面试之面试题大全

    转载自:http://blog.csdn.net/lijizhi19950123/article/details/77679489 Java 面试知识点总结 本篇文章会对面试中常遇到的Java技术点进 ...

  4. 面试时,问哪些问题能试出一个 Android 应用开发者真正的水平?【转自知乎】

    这几年面过的各种Android开发也有三位数了,failed的不敢说,pass的基本都没有看走眼,来得晚了也想说说我的体会. 一般面试时间短则30分钟,多则1个小时,这么点时间要全面考察一个人难度很大 ...

  5. 面试时,问哪些问题能试出一个Android应用开发者真正的水平?

    一般面试时间短则30分钟,多则1个小时,这么点时间要全面考察一个人难度很大,需要一些技巧,这里我不局限于回答题主的问题,而是分享一下我个人关于如何做好Android技术面试的一些经验: 面试前的准备 ...

  6. Amazon前技术副总裁解剖完美技术面试

    Amazon前技术副总裁解剖完美技术面试 投递人 itwriter 发布于 2014-03-03 14:30 评论(0) 有1729人阅读  原文链接  [收藏]  « » 英文原文:The Anat ...

  7. (Java后端 Java web)面试时如何展示自己非技术方面的能力(其实就是综合能力)

    这篇文章的适用范围其实不仅限于Java后端或Java Web,不过其中有些是拿这方面举例的,在其它方面,大家可以举一反三,应该也能得到些启示. 我们在面试时,会发现有些候选人技术不错,比如在Java ...

  8. 以技术面试官的经验分享毕业生和初级程序员通过面试的技巧(Java后端方向)

    本来想分享毕业生和初级程序员如何进大公司的经验,但后来一想,人各有志,有程序员或许想进成长型或创业型公司或其它类型的公司,所以就干脆来分享些提升技能和通过面试的技巧,技巧我讲,公司你选,两厢便利. 毕 ...

  9. 95%的技术面试必考的JVM知识点都在这,另附加分思路!

    概述:知识点汇总 jvm的知识点汇总共6个大方向:内存模型.类加载机制.GC垃圾回收是比较重点的内容.性能调优部分偏重实际应用,重点突出实践能力.编译器优化和执行模式部分偏重理论基础,主要掌握知识点. ...

随机推荐

  1. js push

    $('.main_div').each(function(){ product_id = parseInt($(this).data('id')); product_num = parseInt($( ...

  2. 获取Web.Config中节点的值

    读取webconfig里面的appSetting和connectionString <appSettings> <add key="SiteURL" value= ...

  3. 任务调度 Quartz 学习(三) CronTrigger 表达式

    CronTrigger CronTriggers往往比SimpleTrigger更有用,如果您需要基于日历的概念,而非SimpleTrigger完全指定的时间间隔,复发的发射工作的时间表. CronT ...

  4. Linux Shell下执行sqlplus

    转载自: http://www.cnblogs.com/include/archive/2011/12/30/2307889.html 以下方法解决了在linux下自动的删除创建用户 sqlplus ...

  5. 【BZOJ】1031 [JSOI2007]字符加密Cipher

    [算法]后缀数组 [题解]把数组复制一遍然后SA处理即可. 后缀数组 #include<cstdio> #include<algorithm> #include<cstr ...

  6. 深入理解 JavaScript(五)

    根本没有“JSON 对象”这回事! 前言 写这篇文章的目的是经常看到开发人员说:把字符串转化为 JSON 对象,把 JSON 对象转化成字符串等类似的话题,所以把之前收藏的一篇老外的文章整理翻译了一下 ...

  7. 一种通过HTTP传文件出网的姿势

    在外网机器上运行文件服务接收服务 root@kali:~/pentest-script/FileTransfer/HttpServer# python3 SimpleHttpUpload.py Ser ...

  8. nginx 伪静态rewrite

    location正则写法 一个示例:   location = / { # 精确匹配 / ,主机名后面不能带任何字符串 [ configuration A ] } location / { # 因为所 ...

  9. redis基础之开机自启动和监听(二)

    redis安装好后,每次手动启动很不方便,配置开机自启动. 方法一:设置启动命令到/etc/rc.d/rc.local rc.local文件是系统全局脚本文件,会在其他开机进程脚本文件执行完毕后执行该 ...

  10. Filecoin:一种去中心化的存储网络(一)

    开始初步了解学习Filecoin,如下是看白皮书的内容整理. 参考: 白皮书中文版 http://chainx.org/paper/index/index/id/13.html 白皮书英文版 http ...