JavaScript中最常见的三个面试题解析


Posted in Javascript onMarch 04, 2017

前言

本文不是讲述最新的JavaScript库,日常的开发实践或任何新的 ES6 函数。 相反,在讨论JavaScript时,经常会在面试中出现这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个元素,没什么大问题,但如果有人在他们的待办事项列表中添加了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: 在循环内使用闭包(Closures)

闭包常常在面试中出现,以便面试官衡量你对这门语言的熟悉程度,以及是否知道何时使用闭包。

闭包的本质是一个内部函数访问其作用域之外的变量。闭包可以用于实现诸如 私有变量 和 创建工厂函数之类的东西。关于使用闭包的常见面试问题是这样的:

编写一个函数,它将循环遍历整数列表,并在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的值,其在循环结束时为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)来提升页面速度和性能。一个真实的案例,来自 guest post on css-tricks:

在2011年,一个问题在Twitter上被提出:当你滚动Twitter feed时,它会会变得非常慢甚至未响应。John Resig 就这个问题发布了一篇博文,它解释了直接绑定函数到scroll事件上是多么糟糕的事。

函数防抖动(Debouncing) 是解决这个问题的一种方式,通过限制需要经过的时间,直到再次调用函数。一个正确实现函数防抖的方法是:把多个函数放在一个函数里调用,隔一定时间执行一次。这里有一个使用原生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毫秒内全部调用。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
js不能获取隐藏的div的宽度只能先显示后获取
Sep 04 Javascript
手机开发必备技巧:javascript及CSS功能代码分享
May 25 Javascript
BootStrap 智能表单实战系列(二)BootStrap支持的类型简介
Jun 13 Javascript
jQuery滚动监听实现商城楼梯式导航效果
Mar 06 Javascript
vue 2.0组件与v-model详解
Mar 27 Javascript
Angular 4.0学习教程之架构详解
Sep 12 Javascript
javascript 缓冲运动框架的实现
Sep 29 Javascript
js用类封装pop弹窗组件
Oct 08 Javascript
js判断节假日实例代码
Dec 27 Javascript
vue axios整合使用全攻略
May 24 Javascript
electron+vue实现div contenteditable截图功能
Jan 07 Javascript
vue 自定义组件添加原生事件
Apr 21 Vue.js
jQuery插件echarts实现的多折线图效果示例【附demo源码下载】
Mar 04 #Javascript
Vue.js实现多条件筛选、搜索、排序及分页的表格功能
Nov 24 #Javascript
纯JS单页面赛车游戏制作代码分享
Mar 03 #Javascript
JS html时钟制作代码分享
Mar 03 #Javascript
AngularJS路由实现页面跳转实例
Mar 03 #Javascript
vue2笔记 — vue-router路由懒加载的实现
Mar 03 #Javascript
求js数组的最大值和最小值的四种方法
Mar 03 #Javascript
You might like
古巴咖啡 Cubita琥爵咖啡 独特的加勒比海风味咖啡
2021/03/06 新手入门
如何提高MYSQL数据库的查询统计速度 select 索引应用
2007/04/11 PHP
php网站地图生成类示例
2014/01/13 PHP
Ubuntu server 11.04安装memcache及php使用memcache来存储session的方法
2016/05/31 PHP
PHP实现将base64编码字符串转换成图片示例
2018/06/22 PHP
Cookie 注入是怎样产生的
2009/04/08 Javascript
jQuery $.each遍历对象、数组用法实例
2015/04/16 Javascript
jQuery结合ajax实现动态加载文本内容
2015/05/19 Javascript
JavaScript中关联原型链属性特性
2016/02/13 Javascript
JavaScript实现广告弹窗效果
2016/08/09 Javascript
第一次接触神奇的前端框架vue.js
2016/12/01 Javascript
JavaScript栈和队列相关操作与实现方法详解
2018/12/07 Javascript
js笔试题-接收get请求参数
2019/06/15 Javascript
vue基于v-charts封装双向条形图的实现代码
2019/12/09 Javascript
[01:45]IMBATV TI4前线报道-选手到达
2014/07/07 DOTA
用python分割TXT文件成4K的TXT文件
2009/05/23 Python
python文件和目录操作函数小结
2014/07/11 Python
Python contextlib模块使用示例
2015/02/18 Python
在Linux上安装Python的Flask框架和创建第一个app实例的教程
2015/03/30 Python
python调用java模块SmartXLS和jpype修改excel文件的方法
2015/04/28 Python
Python易忽视知识点小结
2015/05/25 Python
老生常谈Python startswith()函数与endswith函数
2017/09/08 Python
Python 查看文件的编码格式方法
2017/12/21 Python
Python机器学习之决策树算法
2017/12/22 Python
利用python将图片转换成excel文档格式
2017/12/30 Python
Python实现的删除重复文件或图片功能示例【去重】
2019/04/23 Python
PyTorch中permute的用法详解
2019/12/30 Python
python实现sm2和sm4国密(国家商用密码)算法的示例
2020/09/26 Python
西班牙英格列斯百货法国官网:El Corte Inglés法国
2017/07/09 全球购物
Infababy英国:婴儿推车、Travel System婴儿车和婴儿汽车座椅销售
2018/05/23 全球购物
捷克街头、运动和滑板一站式商店:BoardStar.cz
2019/10/06 全球购物
管理人员岗位职责
2015/02/14 职场文书
幼儿园托班开学寄语(2015秋季)
2015/05/27 职场文书
《曹冲称象》教学反思
2016/02/20 职场文书
写作之关于描写老人的好段摘抄
2019/11/14 职场文书
怎么禁用Windows 11快照布局? win11不使用快照布局的技巧
2021/11/21 数码科技