10道典型的JavaScript面试题
问题1: 作用域(Scope)
考虑以下代码:
(function() {
var a = b = ;
})();
console.log(b);
上述代码会打印出5。这个问题的陷阱就是,在立即执行函数表达式(IIFE)中,有两个赋值,但是其中变量a使用关键词var来声明。这就意味着a是这个函数的局部变量。与此相反,b被分配给了全局作用域(译注:也就是全局变量)。
这个问题另一个陷阱就是,在函数中没有使用”严格模式” ('use strict';)。如果 严格模式开启,那么代码就会报错 ” Uncaught ReferenceError: b is not defined” 。请记住,如果这是预期的行为,严格模式要求你显式地引用全局作用域。所以,你需要像下面这么写:
(function() {
'use strict';
var a = window.b = ;
})();
console.log(b);
没有var声明的隐式全局变量
问题2: 创建 “原生(native)” 方法
在 String 对象上定义一个 repeatify 函数。这个函数接受一个整数参数,来明确字符串需要重复几次。这个函数要求字符串重复指定的次数。举个例子:
console.log('hello'.repeatify());
应该打印出hellohellohello,一个可行的做法如下:
String.prototype.repeatify = String.prototype.repeatify || function(times) {
var str = '';
for (var i = ; i < times; i++) {
str += this;
}
return str;
};
这个问题测试了开发人员对 javascript 中继承及原型(prototype)属性的知识。这也验证了开发人员是否有能力扩展原生数据类型功能(虽然不应该这么做)。
在这里,另一个关键点是,看你怎样避免重写可能已经定义了的方法。这可以通过在定义自己的方法之前,检测方法是否已经存在。
String.prototype.repeatify = String.prototype.repeatify || function(times) {/* code here */};
当你去扩展一个Javascript方法时,这个技术非常有用。
问题3: 变量提升(Hoisting)
执行以下代码的结果是什么?为什么?
function test() {
console.log(a);
console.log(foo());
var a = ;
function foo() {
return ;
}
}
test();
这段代码的执行结果是undefined 和 2。
这个结果的原因是,变量和函数都被提升(hoisted) 到了函数体的顶部,声明被提升,但是赋值还是在原来的位置。因此,当打印变量a时,它虽存在于函数体(因为a已经被声明),但仍然是undefined。换言之,上面的代码等同于下面的代码:
function test() {
var a;
function foo() {
return ;
}
console.log(a);
console.log(foo());
a = ;
}
test();
问题4: 在javascript中,`this`是如何工作的
以下代码的结果是什么?请解释你的答案。
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()方法强制转换上下文环境。下面的代码中,我用了call(),但apply()也能产生同样的结果:
console.log(test.call(obj.prop));
问题6: 闭包(Closures)
考虑下面的代码:
var nodes = document.getElementsByTagName('button');
for (var i = ; i < nodes.length; i++) {
nodes[i].addEventListener('click', function() {
console.log('You clicked element #' + i);
});
}
请问,如果用户点击第一个和第四个按钮的时候,控制台分别打印的结果是什么?为什么?
代码打印两次You clicked element #NODES_LENGTH,其中NODES_LENGTH是nodes的结点个数。
原因是在for循环完成后,变量i的值等于节点列表的长度。此外,因为i在代码添加处理程序的作用域中,该变量属于处理程序的闭包。你会记得,闭包中的变量的值不是静态的,因此i的值不是添加处理程序时的值(对于列表来说,第一个按钮为0,对于第二个按钮为1,依此类推)。在处理程序将被执行的时候,在控制台上将打印变量i的当前值,等于节点列表的长度。
问题7: 闭包(Closures)
修复上题的问题,使得点击第一个按钮时输出0,点击第二个按钮时输出1,依此类推。
有多种办法可以解决这个问题,下面主要使用两种方法解决这个问题。此外可以看我之前写的一篇博客:关于一道JS面试题的思考
第一个解决方案使用立即执行函数表达式(IIFE)再创建一个闭包,从而得到所期望的i的值。实现此方法的代码如下:
var nodes = document.getElementsByTagName('button');
for (var i = ; i < nodes.length; i++) {
nodes[i].addEventListener('click', (function(i) {
return function() {
console.log('You clicked element #' + i);
}
})(i));
}
另一个解决方案不使用IIFE,而是将函数移到循环的外面,将i作为参数传进。这种方法由下面的代码实现:
function handlerWrapper(i) {
return function() {
console.log('You clicked element #' + i);
}
}
var nodes = document.getElementsByTagName('button');
for (var i = ; i < nodes.length; i++) {
nodes[i].addEventListener('click', handlerWrapper(i));
}
问题8:数据类型
考虑如下代码:
console.log(typeof null);//object
console.log(typeof {});//object
console.log(typeof []);//object
console.log(typeof undefined);//undefined
大多数开发人员认为typeof []会返回Array。如果你想测试一个变量是否为数组
var myArray = [];
if (myArray instanceof Array) {
// do something...
}
问题9:事件循环
下面代码运行结果是什么?请解释。
function printing() {
console.log();
setTimeout(function() { console.log(); }, );
setTimeout(function() { console.log(); }, );
console.log();
}
printing();
输出结果:1 4 3 2
想知道为什么输出顺序是这样的,你需要弄了解setTimeout()做了什么,以及浏览器的事件循环原理。浏览器有一个事件循环用于检查事件队列,处理延迟的事件。UI事件(例如,点击,滚动等),Ajax回调,以及提供给setTimeout()和setInterval()的回调都会依次被事件循环处理。因此,当调用setTimeout()函数时,即使延迟的时间被设置为0,提供的回调也会被排队。回调会呆在队列中,直到指定的时间用完后,引擎开始执行动作(如果它在当前不执行其他的动作)。因此,即使setTimeout()回调被延迟0毫秒,它仍然会被排队,并且直到函数中其他非延迟的语句被执行完了之后,才会执行。
有了这些认识,理解输出结果为“1”就容易了,因为它是函数的第一句并且没有使用setTimeout()函数来延迟。接着输出“4”,因为它是没有被延迟的数字,也没有进行排队。然后,剩下了“2”,“3”,两者都被排队,但是前者需要等待一秒,后者等待0秒(这意味着引擎完成前两个输出之后马上进行)。这就解释了为什么“3”在“2”之前。
问题10:算法
写一个isPrime()函数,当其为质数时返回true,否则返回false。
我认为这是面试中最常见的问题之一。然而,尽管这个问题经常出现并且也很简单,但是从被面试人提供的答案中能很好地看出被面试人的数学和算法水平。
首先, 因为JavaScript不同于C或者Java,因此你不能信任传递来的数据类型。如果面试官没有明确地告诉你,你应该询问他是否需要做输入检查,还是不进行检查直接写函数。严格上说,应该对函数的输入进行检查。
第二点要记住:负数不是质数。同样的,1和0也不是,因此,首先测试这些数字。此外,2是质数中唯一的偶数。没有必要用一个循环来验证4,6,8。再则,如果一个数字不能被2整除,那么它不能被4,6,8等整除。因此,你的循环必须跳过这些数字。如果你测试输入偶数,你的算法将慢2倍(你测试双倍数字)。可以采取其他一些更明智的优化手段,我这里采用的是适用于大多数情况的。例如,如果一个数字不能被5整除,它也不会被5的倍数整除。所以,没有必要检测10,15,20等等。
最后一点,你不需要检查比输入数字的开方还要大的数字。我感觉人们会遗漏掉这一点,并且也不会因为此而获得消极的反馈。但是,展示出这一方面的知识会给你额外加分。
现在你具备了这个问题的背景知识,下面是总结以上所有考虑的解决方案:
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 < ) {
return false;
}
if (number === ) {
return true;
} else if (number % === ) {
return false;
}
var squareRoot = Math.sqrt(number);
for(var i = ; i <= squareRoot; i += ) {
if (number % i === ) {
return false;
}
}
return true;
}
10道典型的JavaScript面试题的更多相关文章
- 10个典型的JavaScript面试题
问题1:作用域 考虑如下代码: JavaScript 1 2 3 4 5 6 7 (function() { var a = b = 5; })(); console.log(b) ...
- 【转】典型的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面试题
这几道题目还是有一点意思的,大家可以研究一番,对自己的技能提升绝对有帮助. 1.调用过程中输出的内容是什么 function fun(n, o) { console.log(o); return { ...
- mysql:10道mysql查询语句面试题
表结构 学生表student(id,name) 课程表course(id,name) 学生课程表student_course(sid,cid,score) 创建表的sql代码 ```sql creat ...
- 10道mysql查询语句面试题
1.https://www.yanxurui.cc/posts/mysql/10-sql-interview-questions/ 2.http://mm.fancymore.com/reading/ ...
- 174道 JavaScript 面试题,助你查漏补缺
最近在整理 JavaScript 的时候发现遇到了很多面试中常见的面试题,本部分主要是作者在 Github 等各大论坛收录的 JavaScript 相关知识和一些相关面试题时所做的笔记,分享这份总结给 ...
随机推荐
- 结构化数据(structured),半结构化数据(semi-structured),非结构化数据(unstructured)
概念 结构化数据:即行数据,存储在数据库里,可以用二维表结构来逻辑表达实现的数据. 半结构化数据:介于完全结构化数据(如关系型数据库.面向对象数据库中的数据)和完全无结构的数据(如声音.图像文件等)之 ...
- 转: Photoshop cs6 快捷键命令大全
转自: http://www.cnblogs.com/zhen656/p/4249759.html 工具箱(多种工具共用一个快捷键的可同时按[Shift]加此快捷键选取) 矩形.椭圆选框工具.单行单列 ...
- Linux下的Backlight子系统(一)【转】
转自:http://blog.csdn.net/weiqing1981127/article/details/8511676 版权所有,转载必须说明转自 http://my.csdn.net/weiq ...
- PL/SQL 02 声明变量 declare
语法:identifier [CONSTANT] datatype [NOT NULL] [:= | DEFAULT expr] identifier:用于指定变量或常量的名称.CONSTANT:用于 ...
- JQ面向对象
静态方法:某种类型才有的方法,这个方法干的事情只有类型本身有关,不受具体实例对象的影响,在C#语言中,它用static表示,VB中用share表示,而在jq中我们一般用$或者JQuery表示JQ类型, ...
- Android6.0获取运行时权限
照着<第一行代码>打代码,然并卵,感叹技术进步的神速.最后提醒一点:IT类的书籍一定要注意出版时间!出版时间!出版时间!重要的事情说三遍 问题出在android6.0的权限获取问题上,以前 ...
- ThinkPHP的静态化页面方法
原来ThinkPHP自带了生成静态页的函数buildHtml,使用起来很方便!最新的手册里没写这个方法,向大家介绍一下. protected function buildHtml($htmlf ...
- 转:Laravel 安装指南
Git 介绍 之所以要说 Git,就是因为 Composre 有时需要用到 Git,还是安装上比较好,Composer 暂且不表,先来了解一下 Git 吧(已经安装的童鞋跳过这里,直接看 Compos ...
- opencv中CV_IMAGE_ELEM的用法读取每个像素
可以使用OpenCV定义的宏来提取象素值假设灰度图像image,存取其i行j列的象素可以这样:CV_IMAGE_ELEM(image, uchar,y, x)如果是彩色图像就是CV_IMAGE_ELE ...
- 51nod 最长单增子序列(动态规划)
最长单增子序列 (LIS Longest Increasing Subsequence)给定一个数列,从中删掉任意若干项剩余的序列叫做它的一个子序列,求它的最长的子序列,满足子序列中的元素是单调递增的 ...