你有必要知道的25个JavaScript面试题


Posted in Javascript onDecember 29, 2015

1、使用 typeof bar === "object" 判断 bar 是不是一个对象有神马潜在的弊端?如何避免这种弊端?

使用 typeof 的弊端是显而易见的(这种弊端同使用 instanceof):

let obj = {};
let arr = [];

console.log(typeof obj === 'object'); //true
console.log(typeof arr === 'object'); //true
console.log(typeof null === 'object'); //true

从上面的输出结果可知,typeof bar === "object" 并不能准确判断 bar 就是一个 Object。可以通过 Object.prototype.toString.call(bar) === "[object Object]" 来避免这种弊端:

let obj = {};
let arr = [];

console.log(Object.prototype.toString.call(obj)); //[object Object]
console.log(Object.prototype.toString.call(arr)); //[object Array]
console.log(Object.prototype.toString.call(null)); //[object Null]

另外,为了珍爱生命,请远离 ==:

你有必要知道的25个JavaScript面试题

而 [] === false 是返回 false 的。

2、下面的代码会在 console 输出神马?为什么?

(function(){
 var a = b = 3;
})();

console.log("a defined? " + (typeof a !== 'undefined')); 
console.log("b defined? " + (typeof b !== 'undefined'));

这跟变量作用域有关,输出换成下面的:

console.log(b); //3
console,log(typeof a); //undefined

拆解一下自执行函数中的变量赋值:

b = 3;
var a = b;

所以 b 成了全局变量,而 a 是自执行函数的一个局部变量。

3、下面的代码会在 console 输出神马?为什么?

var myObject = {
 foo: "bar",
 func: function() {
 var self = this;
 console.log("outer func: this.foo = " + this.foo);
 console.log("outer func: self.foo = " + self.foo);
 (function() {
 console.log("inner func: this.foo = " + this.foo);
 console.log("inner func: self.foo = " + self.foo);
 }());
 }
};
myObject.func();

第一个和第二个的输出不难判断,在 ES6 之前,JavaScript 只有函数作用域,所以 func 中的 IIFE 有自己的独立作用域,并且它能访问到外部作用域中的 self,所以第三个输出会报错,因为 this 在可访问到的作用域内是 undefined,第四个输出是 bar。如果你知道闭包,也很容易解决的:

(function(test) {
 console.log("inner func: this.foo = " + test.foo); //'bar'
 console.log("inner func: self.foo = " + self.foo);
}(self));

如果对闭包不熟悉,可以参考本文:从作用域链谈闭包

4、将 JavaScript 代码包含在一个函数块中有神马意思呢?为什么要这么做?

换句话说,为什么要用立即执行函数表达式(Immediately-Invoked Function Expression)。

IIFE 有两个比较经典的使用场景,一是类似于在循环中定时输出数据项,二是类似于 JQuery/Node 的插件和模块开发。

for(var i = 0; i < 5; i++) {
 setTimeout(function() {
 console.log(i); 
 }, 1000);
}

上面的输出并不是你以为的0,1,2,3,4,而输出的全部是5,这时 IIFE 就能有用了:

for(var i = 0; i < 5; i++) {
 (function(i) {
 setTimeout(function() {
 console.log(i); 
 }, 1000);
 })(i)
}

而在 JQuery/Node 的插件和模块开发中,为避免变量污染,也是一个大大的 IIFE:

(function($) { 
 //代码
 } )(jQuery);

5、在严格模式('use strict')下进行 JavaScript 开发有神马好处?

消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为;
消除代码运行的一些不安全之处,保证代码运行的安全;
提高编译器效率,增加运行速度;
为未来新版本的Javascript做好铺垫。
6、下面两个函数的返回值是一样的吗?为什么?

function foo1()
{
 return {
 bar: "hello"
 };
}

function foo2()
{
 return
 {
 bar: "hello"
 };
}

在编程语言中,基本都是使用分号(;)将语句分隔开,这可以增加代码的可读性和整洁性。而在JS中,如若语句各占独立一行,通常可以省略语句间的分号(;),JS 解析器会根据能否正常编译来决定是否自动填充分号:

var test = 1 + 
2
console.log(test); //3

在上述情况下,为了正确解析代码,就不会自动填充分号了,但是对于 return 、break、continue 等语句,如果后面紧跟换行,解析器一定会自动在后面填充分号(;),所以上面的第二个函数就变成了这样:

function foo2()
{
 return;
 {
 bar: "hello"
 };
}

所以第二个函数是返回 undefined。

7、神马是 NaN,它的类型是神马?怎么测试一个值是否等于 NaN?

NaN 是 Not a Number 的缩写,JavaScript 的一种特殊数值,其类型是 Number,可以通过 isNaN(param) 来判断一个值是否是 NaN:

console.log(isNaN(NaN)); //true
console.log(isNaN(23)); //false
console.log(isNaN('ds')); //true
console.log(isNaN('32131sdasd')); //true
console.log(NaN === NaN); //false
console.log(NaN === undefined); //false
console.log(undefined === undefined); //false
console.log(typeof NaN); //number
console.log(Object.prototype.toString.call(NaN)); //[object Number]

ES6 中,isNaN() 成为了 Number 的静态方法:Number.isNaN().

8、解释一下下面代码的输出

console.log(0.1 + 0.2); //0.30000000000000004
console.log(0.1 + 0.2 == 0.3); //false

JavaScript 中的 number 类型就是浮点型,JavaScript 中的浮点数采用IEEE-754 格式的规定,这是一种二进制表示法,可以精确地表示分数,比如1/2,1/8,1/1024,每个浮点数占64位。但是,二进制浮点数表示法并不能精确的表示类似0.1这样 的简单的数字,会有舍入误差。

由于采用二进制,JavaScript 也不能有限表示 1/10、1/2 等这样的分数。在二进制中,1/10(0.1)被表示为 0.00110011001100110011…… 注意 0011 是无限重复的,这是舍入误差造成的,所以对于 0.1 + 0.2 这样的运算,操作数会先被转成二进制,然后再计算:

0.1 => 0.0001 1001 1001 1001…(无限循环)
0.2 => 0.0011 0011 0011 0011…(无限循环)
双精度浮点数的小数部分最多支持 52 位,所以两者相加之后得到这么一串 0.0100110011001100110011001100110011001100…因浮点数小数位的限制而截断的二进制数字,这时候,再把它转换为十进制,就成了 0.30000000000000004。

对于保证浮点数计算的正确性,有两种常见方式。

一是先升幂再降幂:

function add(num1, num2){
 let r1, r2, m;
 r1 = (''+num1).split('.')[1].length;
 r2 = (''+num2).split('.')[1].length;

 m = Math.pow(10,Math.max(r1,r2));
 return (num1 * m + num2 * m) / m;
}
console.log(add(0.1,0.2)); //0.3
console.log(add(0.15,0.2256)); //0.3756

二是是使用内置的 toPrecision() 和 toFixed() 方法,注意,方法的返回值字符串。

function add(x, y) {
 return x.toPrecision() + y.toPrecision()
}
console.log(add(0.1,0.2)); //"0.10.2"

9、实现函数 isInteger(x) 来判断 x 是否是整数

可以将 x 转换成10进制,判断和本身是不是相等即可:

function isInteger(x) { 
 return parseInt(x, 10) === x; 
}

ES6 对数值进行了扩展,提供了静态方法 isInteger() 来判断参数是否是整数:

Number.isInteger(25) // true
Number.isInteger(25.0) // true
Number.isInteger(25.1) // false
Number.isInteger("15") // false
Number.isInteger(true) // false

JavaScript能够准确表示的整数范围在 -2^53 到 2^53 之间(不含两个端点),超过这个范围,无法精确表示这个值。ES6 引入了Number.MAX_SAFE_INTEGER 和 Number.MIN_SAFE_INTEGER这两个常量,用来表示这个范围的上下限,并提供了 Number.isSafeInteger() 来判断整数是否是安全型整数。

10、在下面的代码中,数字 1-4 会以什么顺序输出?为什么会这样输出?

(function() {
 console.log(1); 
 setTimeout(function(){console.log(2)}, 1000); 
 setTimeout(function(){console.log(3)}, 0); 
 console.log(4);
})();

这个就不多解释了,主要是 JavaScript 的定时机制和时间循环,不要忘了,JavaScript 是单线程的。详解可以参考 从setTimeout谈JavaScript运行机制。

11、写一个少于 80 字符的函数,判断一个字符串是不是回文字符串

function isPalindrome(str) {
 str = str.replace(/\W/g, '').toLowerCase();
 return (str == str.split('').reverse().join(''));
}

这个题我在 codewars 上碰到过,并收录了一些不错的解决方式,可以戳这里:Palindrome For Your Dome

12、写一个按照下面方式调用都能正常工作的 sum 方法

console.log(sum(2,3)); // Outputs 5
console.log(sum(2)(3)); // Outputs 5

针对这个题,可以判断参数个数来实现:

function sum() {
 var fir = arguments[0];
 if(arguments.length === 2) {
 return arguments[0] + arguments[1]
 } else {
 return function(sec) {
 return fir + sec;
 }
 }
 
}

13、根据下面的代码片段回答后面的问题

for (var i = 0; i < 5; i++) {
 var btn = document.createElement('button');
 btn.appendChild(document.createTextNode('Button ' + i));
 btn.addEventListener('click', function(){ console.log(i); });
 document.body.appendChild(btn);
}

1、点击 Button 4,会在控制台输出什么?

2、给出一种符合预期的实现方式

  • 1、点击5个按钮中的任意一个,都是输出5
  • 2、参考 IIFE。

14、下面的代码会输出什么?为什么?

var arr1 = "john".split(''); j o h n
var arr2 = arr1.reverse(); n h o j
var arr3 = "jones".split(''); j o n e s
arr2.push(arr3);
console.log("array 1: length=" + arr1.length + " last=" + arr1.slice(-1));
console.log("array 2: length=" + arr2.length + " last=" + arr2.slice(-1));

会输出什么呢?你运行下就知道了,可能会在你的意料之外。

 reverse() 会改变数组本身,并返回原数组的引用。

slice 的用法请参考:slice

15、下面的代码会输出什么?为什么?

console.log(1 + "2" + "2");
console.log(1 + +"2" + "2");
console.log(1 + -"1" + "2");
console.log(+"1" + "1" + "2");
console.log( "A" - "B" + "2");
console.log( "A" - "B" + 2);

输出什么,自己去运行吧,需要注意三个点:

多个数字和数字字符串混合运算时,跟操作数的位置有关

console.log(2 + 1 + '3'); / /‘33'
console.log('3' + 2 + 1); //'321'

数字字符串之前存在数字中的正负号(+/-)时,会被转换成数字

console.log(typeof '3'); // string
console.log(typeof +'3'); //number

同样,可以在数字前添加 '',将数字转为字符串

console.log(typeof 3); // number
console.log(typeof (''+3)); //string

对于运算结果不能转换成数字的,将返回 NaN

console.log('a' * 'sd'); //NaN
console.log('A' - 'B'); // NaN

这张图是运算转换的规则

你有必要知道的25个JavaScript面试题16、

如果 list 很大,下面的这段递归代码会造成堆栈溢出。如果在不改变递归模式的前提下修善这段代码?

var list = readHugeList();

var nextListItem = function() {
 var item = list.pop();

 if (item) {
 // process the list item...
 nextListItem();
 }
};

原文上的解决方式是加个定时器:

var list = readHugeList();

var nextListItem = function() {
 var item = list.pop();

 if (item) {
 // process the list item...
 setTimeout( nextListItem, 0);
 }
};

解决方式的原理请参考第10题。

17、什么是闭包?举例说明

可以参考此篇:从作用域链谈闭包

18、下面的代码会输出什么?为啥?

for (var i = 0; i < 5; i++) {
 setTimeout(function() { console.log(i); }, i * 1000 );
}

请往前面翻,参考第4题,解决方式已经在上面了

19、解释下列代码的输出

console.log("0 || 1 = "+(0 || 1));
console.log("1 || 2 = "+(1 || 2));
console.log("0 && 1 = "+(0 && 1));
console.log("1 && 2 = "+(1 && 2));

逻辑与和逻辑或运算符会返回一个值,并且二者都是短路运算符:

逻辑与返回第一个是 false 的操作数 或者 最后一个是 true的操作数

console.log(1 && 2 && 0); //0
console.log(1 && 0 && 1); //0
console.log(1 && 2 && 3); //3

如果某个操作数为 false,则该操作数之后的操作数都不会被计算

逻辑或返回第一个是 true 的操作数 或者 最后一个是 false的操作数

console.log(1 || 2 || 0); //1
console.log(0 || 2 || 1); //2
console.log(0 || 0 || false); //false

如果某个操作数为 true,则该操作数之后的操作数都不会被计算

如果逻辑与和逻辑或作混合运算,则逻辑与的优先级高:

console.log(1 && 2 || 0); //2
console.log(0 || 2 && 1); //1
console.log(0 && 2 || 1); //1

在 JavaScript,常见的 false 值:

0, '0', +0, -0, false, '',null,undefined,null,NaN
要注意空数组([])和空对象({}):

console.log([] == false) //true
console.log({} == false) //false
console.log(Boolean([])) //true
console.log(Boolean({})) //true

所以在 if 中,[] 和 {} 都表现为 true:

你有必要知道的25个JavaScript面试题

20、解释下面代码的输出

console.log(false == '0')
console.log(false === '0')

请参考前面第14题运算符转换规则的图。

21、解释下面代码的输出

var a={},
 b={key:'b'},
 c={key:'c'};

a[b]=123;
a[c]=456;

console.log(a[b]);

输出是456。

22、解释下面代码的输出

console.log((function f(n){return ((n > 1) ? n * f(n-1) : n)})(10));

结果是10的阶乘。这是一个递归调用,为了简化,我初始化 n=5,则调用链和返回链如下:

你有必要知道的25个JavaScript面试题

23、解释下面代码的输出

(function(x) {
 return (function(y) {
 console.log(x);
 })(2)
})(1);

输出1,闭包能够访问外部作用域的变量或参数。

24、解释下面代码的输出,并修复存在的问题

var hero = {
 _name: 'John Doe',
 getSecretIdentity: function (){
 return this._name;
 }
};

var stoleSecretIdentity = hero.getSecretIdentity;

console.log(stoleSecretIdentity());
console.log(hero.getSecretIdentity());

将 getSecretIdentity 赋给 stoleSecretIdentity,等价于定义了 stoleSecretIdentity 函数:

var stoleSecretIdentity = function (){
 return this._name;
}
stoleSecretIdentity

的上下文是全局环境,所以第一个输出 undefined。若要输出 John Doe,则要通过 call 、apply 和 bind 等方式改变 stoleSecretIdentity 的this 指向(hero)。

第二个是调用对象的方法,输出 John Doe。

25、给你一个 DOM 元素,创建一个能访问该元素所有子元素的函数,并且要将每个子元素传递给指定的回调函数。

函数接受两个参数:

  • DOM
  • 指定的回调函数

原文利用 深度优先搜索(Depth-First-Search) 给了一个实现:

function Traverse(p_element,p_callback) {
 p_callback(p_element);
 var list = p_element.children;
 for (var i = 0; i < list.length; i++) {
 Traverse(list[i],p_callback); // recursive call
 }
}

以上就是为大家分享的25个JavaScript面试题,希望对大家参加面试有所帮助。

Javascript 相关文章推荐
JS 学习笔记 防止发生命名冲突
Jul 30 Javascript
jQuery 获取对象 根据属性、内容匹配, 还有表单元素匹配
May 31 Javascript
jquery $.ajax相关用法分享
Mar 16 Javascript
js工具方法弹出蒙版
May 08 Javascript
javascript scrollTop正解使用方法
Nov 14 Javascript
纯js代码实现未知宽高的元素在指定元素中垂直水平居中显示
Sep 12 Javascript
功能强大的Bootstrap效果展示(二)
Aug 03 Javascript
关于动态执行代码(js的Eval)实例详解
Aug 15 Javascript
jQuery动态修改字体大小的方法【测试可用】
Sep 09 Javascript
js字符串操作总结(必看篇)
Nov 22 Javascript
深入理解JavaScript创建对象的多种方式以及优缺点
Jun 01 Javascript
vue同个按钮控制展开和折叠同个事件操作
Jul 29 Javascript
JavaScript仿支付宝密码输入框
Dec 29 #Javascript
js实现商城星星评分的效果
Dec 29 #Javascript
原生js配合cookie制作保存路径的拖拽
Dec 29 #Javascript
一种新的javascript对象创建方式Object.create()
Dec 28 #Javascript
JavaScrip常见的一些算法总结
Dec 28 #Javascript
简单介绍JavaScript数据类型之隐式类型转换
Dec 28 #Javascript
Bootstrap导航栏各元素操作方法(表单、按钮、文本)
Dec 28 #Javascript
You might like
Cappuccino 卡布其诺咖啡之制作
2021/03/03 冲泡冲煮
深入理解:XML与对象的序列化与反序列化
2013/06/08 PHP
PHP实现动态web服务器方法
2015/07/29 PHP
深入浅析用PHP实现MVC
2016/03/02 PHP
php抽奖概率算法(刮刮卡,大转盘)
2020/04/17 PHP
PHP观察者模式定义与用法实例分析
2019/03/22 PHP
Add Formatted Text to a Word Document
2007/06/15 Javascript
javascript高亮效果的二种实现方法
2008/09/14 Javascript
Chosen 基于jquery的选择框插件使用方法
2012/05/30 Javascript
JavaScript中的getMilliseconds()方法使用详解
2015/06/10 Javascript
js实现将选中内容分享到新浪或腾讯微博
2015/12/16 Javascript
jquery实现列表上下移动功能
2016/02/25 Javascript
jQuery基于ajax方式实现用户名存在性检查功能示例
2017/02/10 Javascript
微信小程序网络请求wx.request详解及实例
2017/05/18 Javascript
jQuery图片加载失败替换默认图片方法汇总
2017/11/29 jQuery
关于Vue组件库开发详析
2018/07/01 Javascript
react实现复选框全选和反选组件效果
2020/08/25 Javascript
JS实现页面侧边栏效果探究
2021/01/08 Javascript
[07:48]DOTA2上海特级锦标赛主赛事首日RECAP
2016/03/04 DOTA
Python实现聊天机器人的示例代码
2018/07/09 Python
Python实现12306火车票抢票系统
2019/07/04 Python
Python实现生成密码字典的方法示例
2019/09/02 Python
python 遗传算法求函数极值的实现代码
2020/02/11 Python
CSS3 制作旋转的大风车(充满童年回忆)
2013/01/30 HTML / CSS
英国评分最高的女性剃须刀订阅盒:FFS Beauty
2018/01/25 全球购物
工程班组长岗位职责
2013/12/30 职场文书
广告学专业自荐信范文
2014/02/24 职场文书
本科毕业自我鉴定
2014/03/20 职场文书
优秀毕业生的求职信
2014/07/21 职场文书
俞敏洪一分钟演讲稿
2014/08/26 职场文书
大连导游词
2015/02/12 职场文书
刑事起诉书范文
2015/05/19 职场文书
主题班会开场白
2015/06/01 职场文书
党课主持词大全
2015/06/30 职场文书
2015中秋祝酒词
2015/08/12 职场文书
python3.9之你应该知道的新特性详解
2021/04/29 Python