JavaScript 是所有现代浏览器的官方语言。因此,各种语言的开发者面试中都会遇到 JavaScript 问题。

本文不讲最新的 JavaScript 库,通用开发实践,或任何新的 ES6 函数。而是讲讲面试中经常出现的 3 个 JavaScript 问题。我问过这些问题,我的朋友说他们也问。

当然不是说你在准备 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 项时没有问题,但如果有人给代办事项列表新增了 10,000 个事项呢(他们也许有一大堆事情要做)?那时函数会创建 10,000 个事件监听器,然后把它们都添加到 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: 在循环中使用闭包

面试中经常会问到闭包,因为面试官能通过这个问题的回答判断你对语言的熟悉程度,以及考察你是否知道什么时候使用闭包。

闭包就是能访问作用域外部变量的内部函数 。闭包能用来实现私有化和创建工厂函数等作用。关于闭包的常见面试题是这样的:

写一个函数,循环一个整数数组,延迟 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 后最终停在了 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 秒之内被触发数千次。这会导致非常严重的性能问题。

如果在面试中讨论到构建应用程序,以及滚动事件,窗口调整事件,或者键盘事件等,请务必提及 debouncing 或者 throttling,作为提高页面速度与性能的方法。来一个 css-tricks 的实例:

2011 年,Twitter 出了一个问题:当滚动 Twitter 摘要时,页面变的很卡甚至无响应。John Resig 写了一篇关于这个问题的博客,解释了直接将耗时的函数绑定在 scroll 事件上是一个多么糟糕的想法。

Debouncing 是解决这个问题的一种方法,它的做法是限制下次函数调用之前必须等待的时间间隔。正确实现 debouncing 的方法是将若干个函数调用 合成 一次,并在给定时间过去之后仅被调用一次。下面是一个原生 JavaScript 的实现,用到了作用域, 闭包, this, 和 计时事件:

// 将会包装事件的 debounce 函数function debounce(fn, delay) {
 // 维护一个 timer
 let timer = null;
 // 能访问 timer 的闭包
 return function() {
   // 通过 ‘this’ 和 ‘arguments’ 获取函数的作用域和变量
   let context = this;
   let args = arguments;
   // 如果事件被调用,清除 timer 然后重新设置 timer
   clearTimeout(timer);
   timer = setTimeout(function() {
     fn.apply(context, args);
   }, delay);
 }}

这个函数 — 当传入一个事件(fn)时 — 会在经过给定的时间(delay)后执行。

函数这样用:

// 当用户滚动时被调用的函数function foo() {
 console.log('You are scrolling!');}// 在 debounce 中包装我们的函数,过 2 秒触发一次let elem = document.getElementById('container');elem.addEventListener('scroll', debounce(foo, 2000));

Throttling 是与 debouncing 类似的一种技术,但它不是在调用函数之前等待一段时间,throttling 是在较长的时间间隔内调用函数。所以如果一个事件每 100 毫秒被触发 10 次,throttling 会在每隔 2 秒时执行一次这个函数,而不是在 100 毫秒内执行 10 次事件。

更多关于 debouncing 和 throttling 的信息请参考以下文章和教程:

  • JavaScript 中的 Throttling 和 Debouncing

  • Throttling 与 Debouncing 的区别

  • 实例解析 Throttling 和 Debouncing

  • Throttling 函数调用 —— Remy Sharp

面试中注意3个javascript的问题的更多相关文章

  1. 面试中要注意的 3 个 JavaScript 问题

    JavaScript 是 所有现代浏览器 的官方语言.因此,各种语言的开发者面试中都会遇到 JavaScript 问题. 本文不讲最新的 JavaScript 库,通用开发实践,或任何新的 ES6 函 ...

  2. 前端js面试中的常见的算法问题

    虽说我们很多时候前端很少有机会接触到算法.大多都交互性的操作,然而从各大公司面试来看,算法依旧是考察的一方面.实际上学习数据结构与算法对于工程师去理解和分析问题都是有帮助的.如果将来当我们面对较为复杂 ...

  3. 第61节:Java中的DOM和Javascript技术

    Java中的DOM和Javascript技术 DOM是一门技术,是文档对象模型.所需的文档只有标记型文档,如我们所学的html文档(文档中的所有标签都封装成为对象了) DOM: 为Document O ...

  4. 解密面试中的套路,你都get到了么?

    如果大家有关注一些测试类的公众号或者论坛的话,肯定会发现很多文章都在表示现在行业的寒冬冷潮来了!然后有很多测试行业从业者,或者转行测试行业者都表示:工作好难找,公司跑了千千万,依然拿不到一个offer ...

  5. 前端面试回顾(1)---javascript的面向对象

    前言 前一阵面试,过程中发现问到一些很基础的问题时候,自己并不能很流畅的回答出来.或者遇到一些基础知识的应用,由于对这些点理解的不是很深入,拿着笔居然什么都写不出来,于是有了回顾一下这些基础知识的想法 ...

  6. 在面试中忽然发现DateTime的一些...

    今天说说我面试中碰到的一个小问题,在我问起DateTime为什么无法赋值NULL值,一般第一反应都认为它是值类型,不是引用类型,但随后我查阅了度娘自我学习到它是结构类型,那么随之而然就无法赋值NULL ...

  7. 面试中关于Java你所需知道的的一切

    本篇文章会对面试中常遇到的Java技术点进行全面深入的总结,帮助我们在面试中更加得心应手,不参加面试的同学也能够借此机会梳理一下自己的知识体系,进行查漏补缺. 1. Java中的原始数据类型都有哪些, ...

  8. JAVA面试中问及HIBERNATE与 MYBATIS的对比,在这里做一下总结

    我是一名java开发人员,hibernate以及mybatis都有过学习,在java面试中也被提及问道过,在项目实践中也应用过,现在对hibernate和mybatis做一下对比,便于大家更好的理解和 ...

  9. 【译】用jQuery 处理XML--浏览器中的XML与JavaScript

    用jQuery 处理XML--写在前面的话 用jQuery 处理XML-- DOM(文本对象模型)简介 用jQuery 处理XML--浏览器中的XML与JavaScript 用jQuery 处理XML ...

随机推荐

  1. 前端HTML以及HTML5(基本标签)

    前面一章介绍了一下前端的发展,这章简单介绍一下html的发展以及基本的标签. 一.HTML的发展史 1.概念 超文本标记语言(HyperText Markup Language,简称HTML)是为 [ ...

  2. arcgis jsapi接口入门系列(9):可以同时显示多个的地图popup

    jsapi有提供popup功能,但缺点很多,例如地图上只能同时显示一个popup,popup内容有限制等 本文提供另一个方法,原理不用jsapi,在地图外用一个普通的div放在地图上面,再监听地图的鼠 ...

  3. Android 使用NestedScrollView+ViewPager+RecyclerView+SmartRefreshLayout打造酷炫下拉视差效果并解决各种滑动冲突

    如果你还在为处理滑动冲突而发愁,那么你需要静下心来看看这边文章,如果你能彻底理解这篇文章中使用的技术,那么,一切滑动冲突的问题解决起来就轻而易举了: 先扔一个最终实现的效果图 先分析下效果图中实现的功 ...

  4. Python各种参数类型

    1. Python的参数传递是值传递还是引 举例说明Python函数参数传递的几种形式,并说明函数传参是值传递还是引用传递 一.位置参数 调用函数时根据函数定义的参数位置来传递参数.例子: def p ...

  5. 【Python图像特征的音乐序列生成】生成伴奏旋律(附部分代码)

    做了半天做的都是一些细枝末节的东西,嗨呀. 伴奏旋律是Ukulele和弦,MIDI发音乐器是Guitar.在弹唱的时候,Ukulele和弦就是伴奏. 我们以创建<成都>伴奏为例: 节奏型: ...

  6. 使用ErrorProvider组件验证文本框输入

    实现效果: 知识运用: ErrorProvider组件的BlinkStyle属性 //指示错误图标的闪烁时间 public ErrorBlinkStyle BlinkStyle{ get;set; } ...

  7. 变量和数据类型&运算符

    变量和数据类型&运算符 变量 变量的作用:用来存储数据 变量命名的规范:字(字符串)下(_下划线)美($)人(¥) 数 (可以包括数字)骆驼 有意义(可以以字母,下划线,美元符号,人民币符号开 ...

  8. superset docker 部署

    公众号原文有更多效果图哦 一.使用自己的数据库 1. 拉取项目 // 创建目录用于存放项目 mkdir -p /mnt/superset cd /mnt/superset git clone http ...

  9. java基础—抽象类介绍

    一.抽象类介绍

  10. 用C#(ASP.Net)在Exchange Server环境下发送邮件

    普通的邮件, 用System.Net.Mail 类 或 System.Web.Mail 类 处理即可, 但是Exchange Server 环境下, 这两个类起不了作用-------至少目前我看到的情 ...