10个典型的JavaScript面试题
问题1:作用域
考虑如下代码:
JavaScript
1
2
3
4
5
6
7
|
(function() {
var a = b = 5;
})();
console.log(b);
|
请问控制台上会输出什么?
答案
输出:5
这一题的陷阱是,在函数表达式中有两个赋值,但a是用关键字var 来声明的,这意味着a是局部变量,而b则被赋予为全局变量。
另一个陷阱是,它并没有使用严格模式(use strict)。在函数里面,如果启用了严格模式,代码就会报错:“Uncaught ReferenceError: b is not defined”。请记住,严格模式需要你显式地引用全局作用域,代码应该写成:
1
2
3
4
5
6
7
8
9
|
(function() {
'use strict';
var a = window.b = 5;
})();
console.log(b);
|
问题2:创建“内置”方法
给String对象定义一个repeatify方法。该方法接收一个整数参数,作为字符串重复的次数,最后返回重复指定次数的字符串。例如:
1
|
console.log('hello'.repeatify(3));
|
输出应该是
1
|
hellohellohello.
|
答案
一个可行的做法如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
String.prototype.repeatify = String.prototype.repeatify || function(times) {
var str = '';
for (var i = 0; i < times; i++) {
str += this;
}
return str;
};
|
这题测试开发者对Javascript的继承及原型属性的知识,它同时也检验了开发者是否能扩展内置数据类型的方法。
这里的另一个关键点是,看你怎样避免重写可能已经定义了的方法。这可以通过在定义自己的方法之前,检测方法是否已经存在。
1
|
String.prototype.repeatify = String.prototype.repeatify || function(times) {/* code here */};
|
当你被问起去扩展一个Javascript方法时,这个技术非常有用。
问题3 :声明提前
下面这段代码的结果是什么?为什么?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
function test() {
console.log(a);
console.log(foo());
var a = 1;
function foo() {
return 2;
}}
test();
|
答案
代码的运行结果:undefined和 2
理由是,变量和函数的声明都被提前至函数体的顶部,而同时变量并没有被赋值。因此,当打印变量a时,它虽存在于函数体(因为a已经被声明),但仍然是undefined。换句话说,上面的代码等同于下面的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
function test() {
var a;
function foo() {
return 2;
}
console.log(a);
console.log(foo());
a = 1;
}
test();
|
问题4:JavaScript中的this
下面代码的运行结果是什么并做解释。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
var fullname = 'John Doe';
var obj = {
fullname: 'Colin Ihrig',
prop: {
fullname: 'Aurelio De Rosa',
getFullname: function() {
return this.fullname;
}}};
console.log(obj.prop.getFullname());
var test = obj.prop.getFullname;
console.log(test());
|
答案
代码输出:Aurelio De Rosa 和 John Doe
理由是,Javascript中关键字this所指代的函数上下文,取决于函数是怎样被调用的,而不是怎样被定义的。
在第一个console.log(),getFullname()被作为obj.prop对象被调用。因此,当前的上下文指代后者,函数返回这个对象的fullname属性。相反,当getFullname()被赋予test变量,当前的上下文指代全局对象window,这是因为test被隐式地作为全局对象的属性。基于这一点,函数返回window的fullname,在本例中即为代码的第一行。
问题5:call()和apply()
修复前一个问题,让最后一个console.log() 打印输出Aurelio De Rosa.
答案
这个问题可以通过运用call()或者apply()方法强制转换上下文环境。如果你不了解这两个方法及它们的区别,我建议你看看这篇文章 What’s the difference between function.call and function.apply?. 下面的代码中我用了call(),但apply()也能产生同样的结果:
1
|
console.log(test.call(obj.prop));
|
问题6:闭包
考虑下面的代码:
1
2
3
4
5
6
|
var nodes = document.getElementsByTagName('button');
for (var i = 0; i < nodes.length; i++) {
nodes[i].addEventListener('click', function() {
console.log('You clicked element #' + i);
});
}
|
请问,如果用户点击第一个和第四个按钮,控制台上会输出什么?为什么?
答案
上面代码的目的在于检测JavaScript的一个重要概念:闭包。对于每一个JavaScript开发者来说,如果你想在网页中编写5行以上的代码,那么准确理解和恰当使用闭包是非常重要的。如果你想开始学习或者只是想简单地温习一下闭包,那么我强烈建议你去阅读这个教程:Colin Ihrig 写的JavaScript Closures Demystified 。
好了,回到上面的代码。控制台会输出两次You clicked element #NODES_LENGTH,其中#NODES_LENGTH等于nodes的结点个数。由于闭包中变量的值不是静态的,i的值并不是添加click事件处理器时的值(比如,当给第一个button添加click事件处理器时i为0,给第二个添加时i为1)。当for循环结束时,变量i的值等于nodes的长度。因此事件被执行时,控制台会输出变量i当前的值,即等于nodes的长度。
问题7:闭包
修复上题的问题,使得点击第一个按钮时输出0,点击第二个按钮时输出1。
答案
有多种办法解决这个问题,下面我给出其中的两种。
第一个解决方案要用到一个IIFE来创建另外一个闭包,从而得到所希望的i的值。相应的代码如下:
1
2
3
4
5
6
7
8
|
var nodes = document.getElementsByTagName('button');
for (var i = 0; i < nodes.length; i++) {
nodes[i].addEventListener('click', (function(i) {
return function() {
console.log('You clicked element #' + i);
}
})(i));
}
|
另一个解决方案不使用IIFE,而是将函数移到循环的外面,代码如下:
1
2
3
4
5
6
7
8
9
10
|
function handlerWrapper(i) {
return function() {
console.log('You clicked element #' + i);
}
}
var nodes = document.getElementsByTagName('button');
for (var i = 0; i <nodes.length; i++) {
nodes[i].addEventListener('click', handlerWrapper(i));
}
|
问题8:数据类型
考虑如下代码:
1
2
3
4
|
console.log(typeof null);
console.log(typeof {});
console.log(typeof []);
console.log(typeof undefined);
|
答案
这一个问题看起来似乎有点傻,但是它测试了typeof 操作符的知识。很多JavaScript开发者并没有意识到typeof的独特性。在本例中,控制台会输出下面的内容:
1
2
3
4
|
object
object
object
undefined
|
最让人吃惊的输出结果可能是第三个,许多开发者认为typeof [ ] 会返回Array。如果你想测试变量是否为数组,可以写下面的代码:
1
2
3
4
5
6
7
|
var myArray = [];
if (myArray instanceof Array) {
console.log("myArray is an instance of Array.");
}
else{
console.log("myArray is not an instance of Array.");
}
|
问题9:事件循环
下面代码运行结果是什么?请解释。
1
2
3
4
5
6
7
|
function printing() {
console.log(1);
setTimeout(function() { console.log(2); }, 1000);
setTimeout(function() { console.log(3); }, 0);
console.log(4);
}
printing();
|
答案
输出结果:
1
2
3
4
|
1
4
3
2
|
要弄懂数字为何以这种顺序输出,你需要弄明白setTimeout()是干什么的,以及浏览器的事件循环工作原理。浏览器有一个事件循环用于检查事件队列,处理延迟的事件。UI事件(例如,点击,滚动等),Ajax回调,以及提供给setTimeout()和setInterval()的回调都会依次被事件循环处理。因此,当调用setTimeout()函数时,即使延迟的时间被设置为0,提供的回调也会被排队。回调会呆在队列中,直到指定的时间用完后,引擎开始执行动作(如果它在当前不执行其他的动作)。因此,即使setTimeout()回调被延迟0毫秒,它仍然会被排队,并且直到函数中其他非延迟的语句被执行完了之后,才会执行。
有了这些认识,理解输出结果为“1”就容易了,因为它是函数的第一句并且没有使用setTimeout()函数来延迟。接着输出“4”,因为它是没有被延迟的数字,也没有进行排队。然后,剩下了“2”,“3”,两者都被排队,但是前者需要等待一秒,后者等待0秒(这意味着引擎完成前两个输出之后马上进行)。这就解释了为什么“3”在“2”之前。
【译者推荐扩展阅读:阮一峰文章JavaScript 运行机制详解:再谈Event Loop 】
问题10:算法
写一个判断质数的isPrime()函数,当其为质数时返回true,否则返回false。
答案
我认为这是在面试中最常问到的一个问题。尽管这个问题反复出现并且也很简单,但是从候选人提供的答案中能很好地看出候选人的数学和算法能力水平。
首先, 因为JavaScript不同于C或者Java,因此你不能信任传递来的数据类型。如果面试官没有明确地告诉你,你应该询问他是否需要做输入检查,还是不进行检查直接写函数。严格上说,应该对函数的输入进行检查。
需要记住的第二点,负数不是质数。同样的,1和0都不是,因此,要对这些数字做检测。另外,2是唯一的既是偶数又是质数的数字。没有必要用一个循环来验证4,6,8。再者,如果一个数字不能被2整除,它同样也不能被4,6,8等整除,因此你的循环需要跳过这些数字。可以采取其他一些更明智的优化手段,我这里采用的是适用于大多数情况的。例如,如果一个数字不能被5整除,它也不会被5的倍数整除。所以,没有必要检测10,15,20等等。如果你深入了解这个问题的解决方案,我建议你去看相关的Wikipedia介绍。
最后一点,你不需要检查比输入数字的开方还要大的数字。我感觉人们会遗漏掉这一点,并且也不会因为此而获得消极的反馈。但是,展示出这一方面的知识会给你额外加分。
现在你具备了这个问题的背景知识,下面是总结以上所有考虑的解决方案:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
function isPrime(number) {
// If your browser doesn't support the method Number.isInteger of ECMAScript 6,
// you can implement your own pretty easily
if (typeof number !== 'number' || !Number.isInteger(number)) {
// Alternatively you can throw an error.
return false;
}
if (number < 2) {
return false;
}
if (number === 2) {
return true;
} else if (number % 2 === 0) {
return false;
}
var squareRoot = Math.sqrt(number);
for(var i = 3; i <= squareRoot; i += 2) {
if (number % i === 0) {
return false;
}
}
return true;
}
|
10个典型的JavaScript面试题的更多相关文章
- 10道典型的JavaScript面试题
问题1: 作用域(Scope) 考虑以下代码: (function() { ; })(); console.log(b); 上述代码会打印出5.这个问题的陷阱就是,在立即执行函数表达式(IIFE)中, ...
- 【转】典型的JavaScript面试题
问题1: 作用域(Scope) (function() { "use strict"; var a = b = 5; })(); console.log(b); 控制台(conso ...
- 五个典型的 JavaScript 面试题
阅读原文 在IT界,需要大量的 JavaScript 开发者.如果你的能力能够胜任这一角色,那么你将获得许多更换工作和提高薪水的机会.但是在你被公司录取之前,你需要展现你的技术实力,以便通过面试环节. ...
- 五个典型的JavaScript面试题
问题1: 范围(Scope) 思考以下代码: 1 2 3 4 5 (function() { var a = b = 5; })(); console.log(b); 控制台(console ...
- 5个典型的JavaScript面试题
在IT界,需要大量的 JavaScript 开发者.如果你的能力能够胜任这一角色,那么你将获得许多换工作和提高薪水的机会.但是在你被公司录取之前,你需要展现你的技术,以便通过面试环节.在这篇文章中,我 ...
- 互联网中级Javascript面试题
互联网中级Javascript面试题 1.实现一个函数clone,可以对JavaScript中的5种主要的数据类型(包括Number.String.Object.Array.Boolean)进行值复制 ...
- 互联网公司前端初级Javascript面试题
互联网公司前端初级Javascript面试题 1.JavaScript是一门什么样的语言,它有哪些特点?(简述javascript语言的特点)JavaScript是一种基于对象(Object)和事件驱 ...
- 174道 JavaScript 面试题,助你查漏补缺
最近在整理 JavaScript 的时候发现遇到了很多面试中常见的面试题,本部分主要是作者在 Github 等各大论坛收录的 JavaScript 相关知识和一些相关面试题时所做的笔记,分享这份总结给 ...
- 你应该知道的25道Javascript面试题
题目来自 25 Essential JavaScript Interview Questions.闲来无事,正好切一下. 一 What is a potential pitfall with usin ...
随机推荐
- HTML模版组件
1.介绍 BootStrap (最好看的) css js [x] 学习 BootStrap 规则 jQueryUI (美观一般,偏后台.*) css js [x] 学习 jQueryUI 规则 Eas ...
- JavaScript面向对象的作用域链(转载)
JavaScript的作用域一直以来是前端开发中比较难以理解的知识点,对于JavaScript的作用域主要记住几句话,走遍天下都不怕... 一.“JavaScript中无块级作用域” 在Java或C# ...
- 用数据说话,R语言有哪七种可视化应用?
今天,随着数据量的不断增加,数据可视化成为将数字变成可用的信息的一个重要方式.R语言提供了一系列的已有函数和可调用的库,通过建立可视化的方式进行数据的呈现.在使用技术的方式实现可视化之前,我们可以先和 ...
- CSS3轻松实现彩色旋转六面体动画
一.效果预览: 二.基本思路: 1.首先这个一个自动触发的动画,因此应使用animation设计,包括自动组装和组装完成后自动旋转的过程: 2.当鼠标放上去的时候六个面及上面的字体均变色,应在六个面设 ...
- #Week7 Neural Networks : Learning
一.Cost Function and Backpropagation 神经网络的损失函数: \[J(\Theta) = - \frac{1}{m} \sum_{i=1}^m \sum_{k=1}^K ...
- 数学--数论--HDU 4675 GCD of Sequence(莫比乌斯反演+卢卡斯定理求组合数+乘法逆元+快速幂取模)
先放知识点: 莫比乌斯反演 卢卡斯定理求组合数 乘法逆元 快速幂取模 GCD of Sequence Alice is playing a game with Bob. Alice shows N i ...
- 洛谷2014 选课(树形DP)树形背包问题
题目描述 在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习,在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习.现在有N门功课,每门课有个学分,每门课有一 ...
- tar命令基本、进阶使用指北
tar命令基本.进阶使用指北 摘要 打包与压缩是我们在计算机系统日常使用中必备的一个工具,就如我们在使用Windows系统,也需要类似WinRAR的压缩软件来将许多数据.文件打包成一个文件,并压缩其占 ...
- 系统基础优化 vim
系统基础优化 vim 1系统基础优化 (CPU-lscpu 内存-free 磁盘-df 负载-w/uptime) 1.1 系统基础优化 准备工作:如何查看系统的信息 (1)cat /etc/redha ...
- 最终父类【根类】:Object类&Objects类
一.java.lang.Object类 1.Object类介绍 Object类是所有类的父类.一个类都会直接或间接继承自该类: 该类中提供了一些非常常用的方法! 2.toString()方法 A: ...