原题来自: javascript-puzzlers
大家可以先去做一下感受感受. 当初我的成绩是 19/44…
第 1 题
1
["1", "2", "3"].map(parseInt);
首先, map
接受两个参数, 一个回调函数 callback
, 一个回调函数的this
值,其中回调函数接受三个参数 currentValue, index, array
;而题目中, map 只传入了回调函数–parseInt.其次, parseInt
只接受两个两个参数 string, radix(基数)
.
- 在没有指定基数,或者基数为 0 的情况下,JavaScript 作如下处理:
- 如果字符串 string 以”0x“或者”0X“开头, 则基数是 16 (16 进制).
- 如果字符串 string 以”0”开头, 基数是 8(八进制)或者 10(十进制),那么具体是哪个基数由实现环境决- 定。ECMAScript 5 规定使用 10,但是并不是所有的浏览器都遵循这个规定。因此,永远都要明确给出
radix
参数的值。- 如果字符串 string 以其它任何值开头,则基数是 10 (十进制)。
parseInt('1', 0);
parseInt('2', 1);
parseInt('3', 2);
所以答案是 [1, NaN, NaN]
第 2 题
1
[typeof null, null instanceof Object];
typeof
返回一个表示类型的字符串。
instanceof
、instanceof
运算符用来检测 constructor.prototype
是否存在于参数 object
的原型链上。
因为 typeof null === 'object'
自语言之初就是这样….
typeof
的结果请看下表:
1
2
3
4
5
6
7
8
9
10
type // result
undefined // "undefined"
null // "object"
Boolean // "boolean"
Number // "number"
String // "string"
Symbol // "symbol"
Host object // Implementation-dependent
Function // "function"
Object // "object"
所以答案 [object, false]
第 3 题
1
[[3, 2, 1].reduce(Math.pow), [].reduce(Math.pow)];
arr.reduce(callback[, initialValue])
reduce
接受两个参数, 一个回调, 一个初始值.
回调函数接受四个参数 previousValue
, currentValue
, currentIndex
, array
需要注意的是 如果数组为空且未提供初始值,则将引发类型错误。
所以第二个表达式会报异常. 第一个表达式等价于 Math.pow(3, 2) => 9; Math.pow(9, 1) =>9
答案 an error
第 4 题
1
2
var val = "smtg";
console.log("Value is " + (val === "smtg") ? "Something" : "Nothing");
简而言之 + 的优先级 大于 ?
所以原题等价于 'Value is true' ? 'Somthing' : 'Nonthing'
而不是 'Value is' + (true ? 'Something' : 'Nonthing')
答案 'Something'
第 5 题
1
2
3
4
5
6
7
8
9
var name = "World!";
(function() {
if (typeof name === "undefined") {
var name = "Jack";
console.log("Goodbye " + name);
} else {
console.log("Hello " + name);
}
})();
javascript作用域中的变量提升。变量提升是 JavaScript 将声明移至作用域 scope (全局域或者当前函数作用域) 顶部的行为。
这题目相当于
1
2
3
4
5
6
7
8
9
10
var name = "World!";
(function() {
var name; // 现在的name是undefined
if (typeof name === "undefined") {
name = "Jack";
console.log("Goodbye " + name);
} else {
console.log("Hello " + name);
}
})();
答案是 Goodbye Jack
第 6 题
1
2
3
4
5
6
7
var END = Math.pow(2, 53);
var START = END - 100;
var count = 0;
for (var i = START; i <= END; i++) {
count++;
}
console.log(count);
js 中可以表示的最大整数不是 2 的 53 次方,而是 1.7976931348623157e+308。 2 的 53 次方不是 js 能表示的最大整数而应该是能正确计算且不失精度的最大整数,可以参见 js 权威指南。 9007199254740992 +1 还是 9007199254740992 ,这就是因为精度问题,如果 9007199254740992 +11 或者 9007199254740992 +111 的话,值是会发生改变的,只是这时候计算的结果不是正确的值,就是因为精度丢失的问题。
它进入无限循环
答案是 other
第 7 题
1
2
3
4
5
var ary = [0, 1, 2];
ary[10] = 10;
ary.filter(function(x) {
return x === undefined;
});
答案是 []
我们来看一下 Array.prototype.filter 的 polyfill:
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
if (!Array.prototype.filter) {
Array.prototype.filter = function(fun /*, thisArg*/) {
"use strict";
if (this === void 0 || this === null) {
throw new TypeError();
}
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);
}
}
}
return res;
};
}
我们看到在迭代这个数组的时候, 首先检查了这个索引值是不是数组的一个属性, 那么我们测试一下.
0 in ary; => true
3 in ary; => false
10 in ary; => true
也就是说 从 3 - 9 都是没有初始化的’坑’!, 这些索引并不存在与数组中. 在 array 的函数调用的时候是会跳过这些’坑’的.
第 8 题
1
2
3
4
var two = 0.2;
var one = 0.1;
var eight = 0.8;
var six = (0.6)[(two - one == one, eight - six == two)];
IEEE 754 标准中的浮点数并不能精确地表达小数
那什么时候精准, 什么时候不经准呢? 笔者也不知道…
答案 [true, false]
第 9 题
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
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"));
// switch 是严格比较, String 实例和 字符串不一样.
var s_prim = "foo";
var s_obj = new String(s_prim);
console.log(typeof s_prim); // "string"
console.log(typeof s_obj); // "object"
console.log(s_prim === s_obj); // false
字符串字面量和直接调用String()方法(不使用new调用构造函数)的结果是原始字符串。JS自动回转化原始字符串到String对象。所以可以在原始字符串上使用用String对象的方法。而在上下文中,在原始字符串的方法被调用或者从其中获取属性时,JS会自动包裹原始字符串然后调用方法或者获取属性。
答案是 Do not know!
第 10 题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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"));
String(x) does not create an object but does return a string, i.e. typeof String(1) === "string"
还是刚才的知识点, 只不过 String 不仅是个构造函数 直接调用返回一个字符串哦.
答案 Case A
第 11 题
1
2
3
4
5
6
7
8
9
10
11
function isOdd(num) {
return num % 2 == 1;
}
function isEven(num) {
return num % 2 == 0;
}
function isSane(num) {
return isEven(num) || isOdd(num);
}
var values = [7, 4, "13", -9, Infinity];
values.map(isSane);
7 % 2 => 1
4 % 2 => 0
'13' % 2 => 1
-9 % % 2 => -1
Infinity % 2 => NaN
需要注意的是 余数的正负号随第一个操作数.
答案 [true, true, true, false, false]
第 12 题
1
2
3
parseInt(3, 8);
parseInt(3, 2);
parseInt(3, 0);
第一个值为3没什么好说的。如果出现的数字不符合后面输入的进制,则为NaN,所以第二个值为NaN。而radix为0时的情况第一题下面有介绍,这里也是一样为默认10
答案 3, NaN, 3
第 13 题
1
Array.isArray(Array.prototype);
一个鲜为人知的实事: Array.prototype => [];
答案: true
第 14 题
1
2
3
4
5
6
var a = [0];
if ([0]) {
console.log(a == true);
} else {
console.log("wut");
}
解析:
- Boolean([0]) === true
- [0] == true
- true 转换为数字 => 1
- [0] 转化为数字失败, 转化为字符串 ‘0’, 转化成数字 => 0
- 0 !== 1 a本身是一个长度为1的数组,而当数组不为空时,其转换成bool值为true。
而==左右的转换,会使用如果一个操作值为布尔值,则在比较之前先将其转换为数值的规则来转换,Number([0]),也就是0,于是变成了0 == true,结果自然是false,所以最终结果为B
答案:false
第 15 题
1
[] == [];
[] 是Object
, 两个 Object
不相等
当==运算符当左右都是对象时,则会比较其是否指向同一个对象。而每次调用字面量创建,都会创造新的对象,也就是会开辟新的内存区域。所以指针的值自然不一样
答案是 false
第 16 题
1
2
"5" + 3;
"5" - 3;
+
用来表示两个数的和或者字符串拼接, -
表示两数之差.
-
会尽可能的将两个操作数变成数字, 而 +
如果两边不都是数字, 那么就是字符串拼接.
答案是 '53', 2
第 17 题
1
2
3
4
5
var arr = Array(3);
arr[0] = 2;
arr.map(function(elem) {
return "1";
});
稀疏数组. 同第 7 题.
题目中的数组其实是一个长度为3
, 但是没有内容的数组, array
上的操作会跳过这些未初始化的’坑’.
所以答案是 ["1", undefined × 2]
第 18 题
1
2
3
4
5
6
7
8
const a = {};
const b = { key: "b" };
const c = { key: "c" };
a[b] = 123;
a[c] = 456;
console.log(a[b]);
对象键自动转换为字符串。我们试图将一个对象设置为对象 a 的键,其值为123。
但是,当对象自动转换为字符串化时,它变成了[Object object]
。所以我们在这里说的a["Object object"] = 123
是。然后,我们可以尝试再次做同样的事情。 c 对象同样会发生隐式类型转换。那么,a["Object object"] = 456
。
然后,我们打印a[b]
,它实际上是a["Object object"]
。我们将其设置为456
,因此返回456
。
答案: 456
第 19 题
1
2
3
4
5
6
7
8
9
function sidEffecting(ary) {
ary[0] = ary[2];
}
function bar(a, b, c) {
c = 10;
sidEffecting(arguments);
return a + b + c;
}
bar(1, 1, 1);
这是一个大坑, 尤其是涉及到 ES6 语法的时候
在调用函数时,函数内部的arguments维护着传递到这个函数的参数列表。它看起来是一个数组,但实际上它只是一个有length属性的Object,不从Array.prototype
继承。所以无法使用一些Array.prototype
的方法。
arguments对象其内部属性以及函数形参创建getter和setter方法,因此改变形参的值会影响到arguments对象的值,反过来也是一样
也就是说 arguments
是一个 object
, c 就是 arguments[2]
, 所以对于 c 的修改就是对 arguments[2]
的修改.
所以答案是 21.
然而!!!!!!
当函数参数涉及到 any rest parameters, any default parameters or any destructured parameters
的时候, 这个 arguments
就不在是一个 mapped arguments object
了…..
1
2
3
4
5
6
7
8
9
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);
答案是: 12
!!!!
第 20 题
1
2
3
var a = 111111111111111110000,
b = 1111;
a + b;
JavaScript 数字的题,与第七题考察点相似。由于 JavaScript 实际上只有一种数字形式 IEEE 754 标准的 64 位双精度浮点数,其所能表示的整数范围为-2^53~2^53
(包括边界值)。这里的 111111111111111110000 已经超过了2^53 次方,所以会发生精度丢失的情况。
答案还是 111111111111111110000
第 21 题
1
2
var x = [].reverse;
x();
这题考查的是函数调用时的 this 和 Array.prototype.reverse 方法。
The reverse method transposes the elements of the calling array object in place, mutating the array, and returning a reference to the array.
也就是说 最后会返回这个调用者(this), 可是 x 执行的时候是上下文是全局. 那么最后返回的是 window. 关于 this 指向问题
答案是 window
第 22 题
1
Number.MIN_VALUE > 0;
MIN_VALUE
属性是 JavaScript 中可表示的最小的数(接近 0 ,但不是负数),它的近似值为 5 x 10-324。
有兴趣的话可以看一下这个MDN
答案是 true
列举 Number 的其它常量:
- Number.MAX_VALUE:最大的正数
- Number.MIN_VALUE:最小的正数
- Number.NaN:特殊值,用来表示这不是一个数
- Number.NEGATIVE_INFINITY:负无穷大
- Number.POSITIVE_INFINITY:正无穷大
第 23 题
1
[1 < 2 < 3, 3 < 2 < 1];
运算符的运算顺序和隐式类型转换的题,从MDN 上运算符优先级,’<’运算符顺序是从左到右,所以变成了[true < 3, false < 1]
1
2
3
4
5
6
3 > 2 && 2 > 1;
// returns true
3 > 2 > 1;
// returns false because 3 > 2 is true, and true > 1 is false
// Adding parentheses makes things clear: (3 > 2) > 1
这个题等价于
1 < 2 => true;
true < 3 => 1 < 3 => true;
3 < 2 => false;
false < 1 => 0 < 1 => true;
所以,这里首先通过Number()转换为数字然后进行比较,true 会转换成 1,而 false 转换成 0,就变成了[1 < 3, 0 < 1]
参考
答案是 [true, true]
第 24 题
1
2
// the most classic wtf
2 == [[[2]]];
又是隐式类型转换的题,
也就是说左右两边都被转换成了字符串,而字符串都是”2”
这里首先需要对==右边的数组进行类型转换,根据以下规则(来自justjavac 的文章《「译」JavaScript 的怪癖 1:隐式类型转换》):
- 调用 valueOf()。如果结果是原始值(不是一个对象),则将其转换为一个数字。
- 否则,调用 toString() 方法。如果结果是原始值,则将其转换为一个数字。
- 否则,抛出一个类型错误。
所以右侧被使用 toString()方法转换为”2”
然后又通过 Number(“2”)转换为数字 2 进行比较,结果就是 true 了
答案是 true
第 25 题
1
2
3
3.toString()
3..toString()
3...toString()
JavaScript 会在调用方法时对原始值进行包装,但在于这个点是小数点、还是方法调用的点,于是乎第一个就是error
了,因为 JavaScript 解释器会将其第一个点认为是小数点。
第二个则很好说通了,第一个点解释为小数点,变成了(3.0).toString(),结果就是"3"
了
第三个也是,第一个点为小数点,第二个是方法调用的点,但是后面接的不是一个合法的方法名,于是乎就error
了
如果
1
2
var a = 3;
a.toString(); // "3"
这是因为在 js 中 1.1
, 1.
, .
` 都是合法的数字. 那么在解析 3.toString 的时候这个** . **到底是属于这个数字还是函数调用呢? 只能是数字, 因为 3.合法啊!
答案是 error, '3', error
第 26 题
1
2
3
4
5
(function() {
var x = (y = 1);
})();
console.log(y);
console.log(x);
变量提升和隐式定义全局变量的题,也是一个 JavaScript 经典的坑…
y 被赋值到全局. x 是局部变量. 所以打印 x 的时候会报 ReferenceError: x is not defined
答案是 1, error
第 27 题
1
2
3
4
var a = /123/,
b = /123/;
a == b;
a === b;
JavaScript 中的正则表达式依旧是对象,==运算符左右两边都是对象时,会比较他们是否指向同一个对象.
答案 false, false
第 28 题
1
2
3
4
5
6
7
var a = [1, 2, 3],
b = [1, 2, 3],
c = [1, 2, 4];
a == b;
a === b;
a > c;
a < c;
JavaScript 中 Array 的本质也是,所以前两个的结果都是 false,
而 JavaScript 中 Array 的’>’运算符和’<’运算符的比较方式类似于字符串比较字典序
会从第一个元素开始进行比较,如果一样比较第二个,还一样就比较第三个,如此类推,所以第三个结果为 false,第四个为 true。
答案 false, false, false, true
第 29 题
1
2
3
var a = {},
b = Object.prototype;
[a.prototype === b, Object.getPrototypeOf(a) === b];
原型链的题(总会有的),考查的__proto__
和prototype
的区别。首先要明确对象和构造函数的关系,对象在创建的时候,其__proto__
会指向其构造函数的prototype
属性
实例对象是没有prototype
属性的,只有函数才有,所以a.prototype
其实是undefined
,第一个结果为 false
Object
实际上是一个构造函数(typeof Object的结果为"function"
),使用字面量创建对象和new Object
创建对象是一样的,所以a.__proto__
也就是Object.prototype
,而Object.getPrototypeOf(a)与a.__proto__
是一样的,所以第二个结果为true
答案 [false, true]
第 30 题
1
2
3
4
function f() {}
var a = f.prototype,
b = Object.getPrototypeOf(f);
a === b;
f.prototype 是使用使用 new 创建的 f 实例的原型
而 Object.getPrototypeOf 是 f 函数的原型.
a === Object.getPrototypeOf(new f()) // true
b === Function.prototype // true
答案 false
第 31 题
1
2
3
4
function foo() {}
var oldName = foo.name;
foo.name = "bar";
[oldName, foo.name];
考察了函数的 name 属性,使用函数定义方式时,会给 function 对象本身添加一个 name 属性,保存了函数的名称,很好理解oldName为"foo"
。name 属性时只读的,不允许修改,所以foo.name = "bar"
;之后,foo.name还是"foo"
,
答案 ['foo', 'foo']
第 32 题
1
"1 2 3".replace(/\d/g, parseInt);
String.prototype.replace
、正则表达式的全局匹配和 parseInt,
首先需要确定 replace 会传给 parseInt 哪些参数。举个栗子:
1
2
3
4
5
6
7
"1 2 3".replace(/\d/g, function() {
console.log(arguments);
});
//输出结果:
//["1", 0, "1 2 3"]
//["2", 2, "1 2 3"]
//["3", 4, "1 2 3"]
一共三个:
- match:正则表达式被匹配到的子字符串
- offset:被匹配到的子字符串在原字符串中的位置
- string:原字符串
这样就很好理解了,又回到之前 parseInt 的问题了,结果就是 parseInt(“1”, 10), parseInt(“2”, 2), parseInt(“3”, 4)
答案: 1, NaN, 3
第 33 题
1
2
3
4
5
6
function f() {}
var parent = Object.getPrototypeOf(f);
f.name; // ?
parent.name; // ?
typeof eval(f.name); // ?
typeof eval(parent.name); // ?
第一 f.name 值为"f"
,第三 eval(“f”)则会输出 f 函数,所以结果为"function"
第二在自己的浏览器测试的时候是 ''
, 第四是 'undefined'
第 34 题
1
2
var lowerCaseOnly = /^[a-z]+$/;
[lowerCaseOnly.test(null), lowerCaseOnly.test()];
正则表达式的test()会自动将参数转换为字符串,原式就变成了[lowerCaseOnly.test("null"), lowerCaseOnly.test("undefined")]
答案:[true, true]
第 35 题
1
[, , ,].join(", ");
[,,,] => [empty × 3]
JavaScript 中使用字面量创建数组时,如果最末尾有一个逗号’,’,会背省略,所以这是个长度为三的稀疏数组(这是长度为三, 并没有 0, 1, 2 三个属性哦)(都是 undefined);
答案: ", , "
第 36 题
1
2
var a = { class: "Animal", name: "Fido" };
a.class;
经典坑中的一个,class 是关键字。根据浏览器的不同,结果不同:
chrome 的结果: “Animal”
Firefox 的结果:”Animal”
Opera 的结果:”Animal”
IE 8 以上也是: “Animal”
IE 8 及以下: 报错
所以答案不重要, 重要的是自己在取属性名称的时候尽量避免保留字. 如果使用的话请加引号 a['class']
第 37 题
1
var a = new Date("epoch");
简单来说, 如果调用 Date 的构造函数传入一个字符串的话需要符合规范, 即满足 Date.parse
的条件.
另外需要注意的是 如果格式错误 构造函数返回的仍是一个 Date 的实例 Invalid Date
.
答案 Invalid Date
第 38 题
1
2
3
var a = Function.length,
b = new Function().length;
a === b;
我们知道一个function(Function 的实例)
的 length 属性就是函数签名的参数个数, 所以 b.length == 0
.
另外 Function.length
定义为 1……
所以不相等……
答案 false
第 39 题
1
2
3
4
var a = Date(0);
var b = new Date(0);
var c = new Date();
[a === b, b === c, a === c];
- 如果不传参数等价于当前时间.
- 如果是函数调用 返回一个字符串.
答案 [false, false, false]
第 40 题
1
2
3
var min = Math.min(),
max = Math.max();
min < max;
Math.min
的参数是 0 个或者多个。如果是多个参数很容易理解,返回参数中最小的。
如果没有参数,则返回Infinity
。Infinity
是 javascript 中全局对象的一个属性,在浏览器环境中就是 window
对象的一个属性,表示无穷大。
而 Math.max()
没有传递参数时返回的是 -Infinity
。
因此 Math.min()
要比 Math.max()
大。
答案 false
第 41 题
1
2
3
4
5
6
7
8
9
10
11
function captureOne(re, str) {
var match = re.exec(str);
return match && match[1];
}
var numRe = /num=(\d+)/gi,
wordRe = /word=(\w+)/i,
a1 = captureOne(numRe, "num=1"),
a2 = captureOne(wordRe, "word=1"),
a3 = captureOne(numRe, "NUM=2"),
a4 = captureOne(wordRe, "WORD=2");
[a1 === a2, a3 === a4];
因为第一个正则有一个 g 选项 它会‘记忆’他所匹配的内容, 等匹配后他会从上次匹配的索引继续, 而第二个正则不会
所以 a1 = ‘1’; a2 = ‘1’; a3 = null; a4 = ‘2’
答案: [true, false]
第 42 题
1
2
3
4
5
6
7
var a = new Date("2014-03-19"),
b = new Date(2014, 03, 19);
[a.getDay() === b.getDay(), a.getMonth() === b.getMonth()];
a.getDay(); // 3
b.getDay(); // 6
a.getMonth(); // 2
b.getMonth(); // 3
new Date("2014-03-19")
如果传入的是一个符合规则的日期字符串(“2014-03-19”),得到的就是对应的标准时间对象 // Wed Mar 19 2014 08:00:00 GMT+0800 (中国标准时间)
new Date(2014, 03, 19)
如果传入的是三个,第三个识别为日期// Wed Mar 19 2014 08:00:00 GMT+0800 (中国标准时间)
答案:[false, false]
第 43 题
1
2
3
4
5
if ("http://giftwrapped.com/picture.jpg".match(".gif")) {
("a gif file");
} else {
("not a gif file");
}
tring.prototype.match
接受一个正则, 如果不是, 按照 new RegExp(obj)
转化. 所以 . 并不会转义
那么 /gif 就匹配了 /.gif/
答案: 'a gif file'
第 44 题
1
2
3
4
5
6
7
8
9
function foo(a) {
var a;
return a;
}
function bar(a) {
var a = "bye";
return a;
}
[foo("hello"), bar("hello")];
在两个函数里, a作为参数其实已经声明了, 所以 var a; var a = 'bye'
其实就是 a; a ='bye'
答案: ["hello", "bye"]
后言
我只是一个分享者,代码的搬运工、一个追求者、一个学习者,有不同的意见或新的想法,提出来,我们一起研究。
鲁迅曾经说过知识遍地,拾到了就是你的
。
类似文章: 悦老板