Fork me on GitHub

Any application that can be written in JavaScript, will eventually be written in JavaScript.

20个JS变态面试题解析

一般工作中使用不到这些麻烦有易错的代码,但是面试的时候有经验的面试官总会来一两个坑爹的题目给你,做好预防,看看这些题目,其他的js陷阱也是异曲同工的。

第一题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*
* 解析:
* map的语法:.map(callback(index,domElement))
* map接收两个参数: map调用者this(aray)和回调函数,回调函数有两个参数:
如果this是普通数组,index是当前遍历的元素,domElement是索引
如果this是jquery对象数组,index是索引,domElement是当前遍历的元素
return: 由回调函数中的返回值组成的新的数组
* parseInt的语法:parseInt(string, radix),
* string必选。要被解析的字符串
* radix可选。解析数字的基数,介于2~36之间,省略或为0则将以10为基数来解析,小于2或大于36 则 parseInt() 将返回 NaN。
* return: 解析后的数字
* 所以本题就是问:
* parseInt('1', 0);
* parseInt('2', 1);
* parseInt('3', 2);
* 所以结果输出: [1, NaN, NaN]
*/
console.info(["1", "2", "3"].map(parseInt));

第二题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*
* 解析:
* typeof 返回一个表示类型的字符串
typeof 的结果请看下面:
**type** **result**
Undefined "undefined"
Null "object"
Boolean "boolean"
Number "number"
String "string"
Symbol "symbol"
Host object Implementation-dependent
Function "function"
Object "object"

instanceof 运算符用来检测 constructor.prototype 是否存在于参数 object 的原型链上
所以输出["object", false]
*/
function two(){
console.info([typeof null, null instanceof Object]); //["object", false]
}

第三题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*
解析:
reduce语法:array.reduce(callbackfn[, initialValue])
array必需。一个数组对象。
callbackfn 必需。
回调函数接受四个参数 previousValue, currentValue, currentIndex, array。
对于数组中的每个元素,reduce 方法都会调用 callbackfn 函数一次。
initialValue 可选。如果指定 initialValue,则它将用作初始值来启动累积。
第一次调用 callbackfn 函数会将此值作为参数而非数组值提供。

当满足下列任一条件时,将引发 TypeError 异常:
1.callbackfn 参数不是函数对象。
2.数组不包含元素,且未提供 initialValue。
Math.pow语法: Math.pow(x,y)。
pow() 方法可返回 x 的 y 次幂的值。

所以第二个表达式会报异常.
第一个表达式等价于 Math.pow(3, 2) => 9; Math.pow(9, 1) =>9
*/
console.info([ [3,2,1].reduce(Math.pow), [].reduce(Math.pow) ]);

第四题

1
2
3
4
5
6
7
8
9

/*
解析:
+ 优先级大于 ?
此题等价于: 'Value is true' ? 'Something' : 'Nothing'
所以结果是:'Something'
*/
var val = 'smtg';
console.log('Value is ' + (val === 'smtg') ? 'Something' : 'Nothing');

第五题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/*
解析:
变量声明提前,在 JavaScript中, functions 和 variables 会被提升。
变量提升是JavaScript将声明移至作用域 scope (全局域或者当前函数作用域) 顶部的行为。
此题相当于:
var name = 'World!';
(function () {
var name;
if (typeof name === 'undefined') {
name = 'Jack';
console.log('Goodbye ' + name);
} else {
console.log('Hello ' + name);
}
})();
所以结果是:Goodbye Jack
*/
var name = 'World!';
(function () {
if (typeof name === 'undefined') {
var name = 'Jack';
console.log('Goodbye ' + name);
} else {
console.log('Hello ' + name);
}
})();

第六题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/*
[JavaScript中的稀疏数组与密集数组][1]

解析:
一般来说,JavaScript中的数组是稀疏的,也就是说,数组中的元素之间可以有空隙
其实在javascript中并没有常规的数组,所有的数组其实就是一个对象。
javascript的数组根本没有索引,因为索引是数字,而js中数组的索引是string,
arr[1]其实就是arr["1"],给arr["1000"] = 1,arr.length也会自动变为1001.
这些表现的根本原因就是,JavaScript中的对象就是字符串到任意值的键值对.注意键只能是字符串.

看一下 Array.prototype.filter 的部分代码:

var t = Object(this);
var len = t.length >>> 0;
if (typeof fun !== 'function') {
throw new TypeError();
}
var res = [];
var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
for (var i = 0; i < len; i++) {
if (i in t) { // 注意这里!!!
var val = t[i];
if (fun.call(thisArg, val, i, t)) {
res.push(val);
}
}
}
从上面可知filter对数组进行遍历时,会首先检查这个索引值是不是数组的一个属性.测试一下:
console.info(0 in ary); //true
console.info(1 in ary); //true
console.info(4 in ary); //false
console.info(10 in ary); // false
也就是说3~9的索引根本没有是初始化

所以答案:[];
*/
var ary = [0,1,2];
ary[10] = 10;
console.info(ary.filter(function(x) { return x === undefined;}));

第七题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/*    
答案:[true, false]
解析:
JavaScript的设计缺陷?浮点运算:0.1 + 0.2 != 0.3
其实在大多数语言里在做浮点运算时都会有丢失精度的问题。
console.info(0.7+0.1);//输出0.7999999999999999
console.info(0.2+0.1);//输出 0.30000000000000004
console.info(0.5+0.1); //输出0.6
那这是js的问题吗?当然不是。你的电脑做着正确的二进制浮点运算,
但问题是你输入的是十进制的数,电脑以二进制运算,
这两者并不是总是转化那么好的,有时候会得到正确的结果,但有时候就不那么幸运了
*/
var two = 0.2
var one = 0.1
var eight = 0.8
var six = 0.6
[two - one == one, eight - six == two]

第八题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/*
解析:
switch 是严格比较。
直接看这个:
var str = 'foo';
var obj = new String(str);

console.log(typeof str); // "string"
console.log(typeof obj); // "object"
console.log(str === obj); // false

答案: 'Do not know!'
*/
function showCase(value) {
switch(value) {
case 'A':
console.log('Case A');
break;
case 'B':
console.log('Case B');
break;
case undefined:
console.log('undefined');
break;
default:
console.log('Do not know!');
}
}
showCase(new String('A'));

第九题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/*
解析:
还是上题的知识点,不过String 不仅是一个构造函数,直接调用会返回一个字符串

答案:'Case A'
*/
function showCase2(value) {
switch(value) {
case 'A':
console.log('Case A');
break;
case 'B':
console.log('Case B');
break;
case undefined:
console.log('undefined');
break;
default:
console.log('Do not know!');
}
}
showCase2(String('A'));

第十题

1
2
3
4
5
6
7
8
9
10
/*
解析: 这题没什么可说的,
console.info([] instanceof Array); // true
console.info([] instanceof Object); // true
[]是一个数组对象. [] == [] 等价于:
var a = [];
var b = [];
a == b; 所以肯定是false
*/
console.log([]==[]); //false

第十一题

1
2
3
4
5
6
7
/*
解析:
+用来表示两个数的和或者字符串拼接, -表示两数之差
- 会尽可能的将两个操作数变成数字, 而 + 如果两边不都是数字, 那么就是字符串拼接
*/
console.info('5' + 3) // 53
console.info('5' - 3) // 2

第十二题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/*
解析:
函数内部的arguments局部变量其实就是该函数的实际参数数组,
所以c就是arguments[2], 也就是说对c的修改就是对arguments[2]的修改。
所以答案是:21, 如果说这个题还可以, 下面第十三题真就有些变天了
*/
function sidEffecting(ary) {
ary[0] = ary[2];
}
function bar(a,b,c) {
c = 10
sidEffecting(arguments);
return a + b + c;
}
bar(1,1,1);

第十二题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*
解析:
当函数参数涉及到 any rest parameters,
any default parameters or any destructured parameters 的时候,
这个 arguments 就不在是一个 mapped arguments object 了.....,
所以答案是12,这个需要好好体会一下
*/
function sidEffecting(ary) {
ary[0] = ary[2];
}
function bar(a,b,c=3) {
c = 10
sidEffecting(arguments);
return a + b + c;
}
bar(1,1,1);

第十三题

1
2
3
4
5
6
7
/*
同第六题 稀疏数组,题中数组长度为3,但是只初始化了一个索引,array上的操作会跳过未初始化的索引
答案:["1", undefined, undefined]
*/
var ary = Array(3);
ary[0]=2
ary.map(function(elem) { return '1'; });

第十四题

1
2
3
4
5
6
7
8
9
10
11
12
/*
解析:
这个题会让人误以为是 2 > 1 && 2 < 3 其实不是的.
这个题相当于:
1 < 2 => true;
true < 3 => 1 < 3 =>true;
3 < 2 => false;
false < 1 => 0 < 1 =>true;

所以答案是:[true, true]
*/
console.info([1 < 2 < 3, 3 < 2 < 1]);

第十五题

1
2
3
4
5
6
7
8
9
10
11
/*
解析:
1、对于string,number等基础类型,==和===是有区别的
1)不同类型间比较,==只比较“转化成同一类型后的值”看“值”是否相等,===如果类型不同,其结果就是不等
2)同类型比较,直接进行“值”比较,两者结果一样
2、对于Array,Object等高级类型,==和===是没有区别的,进行“指针地址”比较
3、基础类型与高级类型,==和===是有区别的
1)对于==,将高级转化为基础类型,进行“值”比较
2)因为类型不同,===结果为false
*/
console.info(2 == [2]); // true

第十六题

1
2
3
4
5
6
7
8
9
10
/*
因为在 js 中 1.1, 1., .1 都是合法的数字.
那么在解析 3.toString 的时候这个 . 到底是属于这个数字还是函数调用呢? 只能是数字, 因为3.合法啊!
*/

3.toString() //error
3..toString() // '3'
3...toString() // error
var a = 3;
a.toString(); // '3'

第十七题

1
2
3
4
5
6
7
8
9
/*
解析:
y 被赋值到全局. x 是局部变量. 所以打印 x 的时候会报 ReferenceError
*/
(function(){
var x = y = 1;
})();
console.log(y); // 1
console.log(x); // error

第十八题

1
2
3
4
5
6
7
8
9
/*
解析:
具体的对象没有prototype属性,所以a.prototype是undefined,
Object.getPrototypeOf(obj) 返回一个具体对象的原型

答案:false, true
*/
var a = {}, b = Object.prototype;
[a.prototype === b, Object.getPrototypeOf(a) === b]

第十九题

1
2
3
4
5
6
7
8
9
10
11
/*
解析:
用new创建f的实例的原型指向 f.prototype,也就是:
f.prototype == Object.getPrototypeOf(new f()); // true
而Object.getPrototypeOf(f)是 f 函数的原型,也就是:
Object.getPrototypeOf(f) == Function.prototype; //true
所以答案是 false
*/
function f() {}
var a = f.prototype, b = Object.getPrototypeOf(f);
a === b

第二十题

1
2
3
4
5
6
7
8
9
10
11
/*
解析:
foo.name 是函数的名字,函数的名字是不可变的,
这里出错的原因是容易把name理解成foo的 static 属性,
我的理解是name是foo函数的固有属性,可以理解为是final的
答案:['foo', 'foo']
*/
function foo() { }
var oldName = foo.name;
foo.name = "bar";
[oldName, foo.name]