44 道 JS 难题,做对一半就是高手

藏宝库编辑 2024-9-19 15:58:33 185 0 来自 中国
1. parseInt 遇上 map

["1", "2", "3"].map(parseInt)// A. ["1", "2", "3"]// B. [1, 2, 3]// C. [0, 1, 2]// D. other答案是D。实际上返回的效果是 [1, NaN, NaN] ,由于 parseInt 函数只须要两个参数 parseInt(value, radix) ,而 map 的回调函数须要三个参数 callback(currentValue, index, array)。
MDN文档中指明 parseInt 第二个参数是一个2到36之间的整数值,用于指定转换中接纳的基数。假如省略该参数或其值为0,则数字将以10为底子来剖析。假如该参数小于2大概大于36,则 parseInt 返回 NaN。别的,转换失败也会返回 NaN。
如今来分析题目。parseInt("1", 0) 的效果是看成十进制来剖析,返回 1;parseInt("2", 1) 的第二个参数非法,返回 NaN;parseInt("3", 2) 在二进制中,"3" 黑白法字符,转换失败,返回 NaN。
2. 神奇的null

[typeof null, null instanceof Object]// A. ["object", false]// B. [null, false]// C. ["object", true]// D. other答案是A。在MDN关于 null 的文档中也特别指出来了,typeof null 的效果是 "object",它是ECMAScript的bug,实在应该是 "null"。但这个bug由来已久,在JavaScript中已经存在了快要二十年,大概永久不会修复,由于这牵涉到太多的Web系统,修复它会产生更多的bug,令许多系统无法正常工作。
而 instanceof 运算符是用来测试一个对象在其原型链构造函数上是否具有 prototype 属性,null 值并不是以 Object 原型创建出来的,以是 null instanceof Object 返回 false。
3. 愤怒的reduce

[ [3,2,1].reduce(Math.pow), [].reduce(Math.pow) ]// A. an error// B. [9, 0]// C. [9, NaN]// D. [9, undefined]答案是A。MDN文档中关于 Array.prototype.reduce() 写得很清楚:
假如数组为空而且没有提供initialValue, 会抛出TypeError 。假如数组仅有一个元素(无论位置如何)而且没有提供initialValue, 大概有提供initialValue但是数组为空,那么此唯一值将被返回而且callback不会被实验。
4. 该死的优先级

var val = 'smtg';console.log('Value is ' + (val === 'smtg') ? 'Something' : 'Nothing');// A. Value is Something// B. Value is Nothing// C. NaN// D. other答案是D。实际上输出 "Something",由于 + 的优先级比条件运算符 condition ? val1 : val2 的优先级高。
5. 神鬼莫测之变量提升

var name = 'World!';(function () {    if (typeof name === 'undefined') {      var name = 'Jack';      console.log('Goodbye ' + name);    } else {      console.log('Hello ' + name);    }})();// A. Goodbye Jack// B. Hello Jack// C. Hello undefined// D. Hello World答案是A。看如下MDN官方文档的表明:
在 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);    }})();6. 死循环陷阱

var END = Math.pow(2, 53);var START = END - 100;var count = 0;for (var i = START; i <= END; i++) {   count++;}console.log(count);// A. 0// B. 100// C. 101// D. other答案是D。在JavaScript中,2^53 是最大的值,没有比这更大的值了。以是 2^53 + 1 == 2^53,以是这个循环无法制止。
7. 过滤器邪术

var ary = [0,1,2];ary[10] = 10;ary.filter(function(x) {  return x === undefined;});// A. [undefined x 7]// B. [0, 1, 2, 10]// C. []// D. [undefined]答案是C。看MDN官方文档的形貌:
filter 为数组中的每个元素调用一次 callback 函数,并使用所有使得 callback 返回 true 或 等价于 true 的值 的元素创建一个新数组。callback 只会在已经赋值的索引上被调用,对于那些已经被删除大概从未被赋值的索引不会被调用。那些没有通过 callback 测试的元素会被跳过,不会被包罗在新数组中。
8. 鉴戒IEEE 754标准

var two = 0.2;var one = 0.1;var eight = 0.8;var six = 0.6;[two - one == one, eight - six == two]// A. [true, false]// B. [false, false]// C. [true, false]// D. other答案是C。JavaScript中接纳双精度浮点数格式,即IEEE 754标准。在该格式下,有些数字无法表现出来,比如:0.1 + 0.2 = 0.30000000000000004 ,这不是JavaScript的锅,所有接纳该标准的语言都有这个题目,比如:Java、Python等。
9. 字符串陷阱

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'));// A. Case A// B. Case B// C. Do not know!// D. undefined答案是C。在 switch 内部使用严酷相称 === 举行判定,而且 new String("A") 返回的是一个对象,而 String("A") 则是直接返回字符串 "A"。你也可以参考MDN中对原始字符串和String对象的区分:
10. 再一次的字符串陷阱

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(String('A'));// A. Case A// B. Case B// C. Do not know!// D. undefined答案显然是A。与上面唯一不同的是没有使用 new 关键字,以是直接返回字符串,实际上,typeof string("A") === "string" 的效果是 true。表明拜见第9条的表明。
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);// A. [true, true, true, true, true]// B. [true, true, true, true, false]// C. [true, true, true, false, false]// D. [true, true, false, false, false]答案是C。-9 % 2 = -1 以及 Infinity % 2 = NaN,求余运算符会生存符号,以是只有 isEven 的判定是可靠的。
12. parseInt小贼

parseInt(3, 8);parseInt(3, 2);parseInt(3, 0);// A. 3, 3, 3// B. 3, 3, NaN// C. 3, NaN, NaN// D. other答案是D。实际效果是 3, NaN, 3,这个在第一个题目中表明的很清楚了。
13. 数组原型是数组

Array.isArray( Array.prototype )// A. true// B. false// C. error// D. other答案是A。一个鲜为人知的究竟:实在 Array.prototype 也是一个数组。这点在MDN文档中提到过。
14. 一言难尽的逼迫转换

var a = [0];if ([0]) {  console.log(a == true);} else {  console.log("wut");}// A. true// B. false// C. "wut"// D. other答案是B。这个是JavaScript中逼迫转换的经典案例,关于逼迫转换不是一两句话可以跟你说清楚的,我发起你系统性的学习一下,推荐你看看《你不知道的JavaScript-中卷》这本书,假如不舍得买书,github上有英文原版:You-Dont-Know-JS,深入明白之后你就是高手了。
好了,回到当前这个题目。当 [0] 须要被逼迫转成 Boolean 的时间会被认为是 true。以是进入第一个 if 语句,而 a == true 的转换规则在ES5规范的第11.9.3节中已经界说过,你可以自己具体探索下。
规范指出,== 相称中,假如有一个使用数是布尔范例,会先把他转成数字,以是比力酿成了 [0] == 1;同时规范指出假如其他范例和数字比力,会实验把这个范例转成数字再举行宽松比力,而对象(数组也是对象)会先调用它的 toString() 方法,此时 [0] 会酿成 "0",然后将字符串 "0" 转成数字 0,而 0 == 1 的效果显然是 false。
15. 撒旦之子“==”

[]==[]// A. true// B. false// C. error// D. other答案是B。ES5规范11.9.3.1-f指出:假如比力的两个对象指向的是同一个对象,就返回 true,否则就返回 false,显然,这是两个不同的数组对象。
16. 加号 VS 减号

'5' + 3;'5' - 3;// A. "53", 2// B. 8, 2// C. error// D. other答案是A。"5" + 2 = "52" 很好明白,+ 运算符中只要有一个是字符串,就会酿成字符串拼接使用。你不知道的是,- 运算符要求两个使用数都是数字,假如不是,会逼迫转换成数字,以是效果就酿成了 5 - 2 = 3。
17. 打死谁人疯子

1 + - + + + - + 1// A. 2// B. 1// C. error// D. other答案是A。这个只能出如今示例代码中,假如你发现哪个疯子写了这个在生产代码中,打死他就行了。你只要知道 + 1 = 1和- 1 = -1,留意符号之间的空格。两个减号抵消,以是最闭幕果等效于 1 + 1 = 2。大概你也可以在符号之间插入 0 来明白,即 1 + 0 - 0 + 0 + 0 + 0 - 0 + 1,如许你就一目了然了吧!万万别写如许的代码,由于大概会被打死!
18. 淘气的map

var ary = Array(3);ary[0] = 2;ary.map(function(elem) {  return "1";});// A. [2, 1, 1]// B. ["1", "1", "1"]// C. [2, "1", "1"]// D. other答案是D。实际上效果是 ["1", undefined x 2],由于规范写得很清楚:
map 方法会给原数组中的每个元素都按次序调用一次 callback 函数。callback 每次实验后的返回值组合起来形成一个新数组。callback 函数只会在有值的索引上被调用;那些从来没被赋过值大概使用 delete 删除的索引则不会被调用。
19. 统统算我的

function sidEffecting(ary) {  ary[0] = ary[2];}function bar(a, b, c) {  c = 10;  sidEffecting(arguments);  return a + b + c;}bar(1, 1, 1);// A. 3// B. 12// C. error// D. other答案是D。实际上效果是 21。在JavaScript中,参数变量和 arguments 是双向绑定的。改变参数变量,arguments 中的值会立即改变;而改变 arguments 中的值,参数变量也会对应改变。
20. 丧失精度的IEEE 754

var a = 111111111111111110000;var b = 1111;console.log(a + b);// A. 111111111111111111111// B. 111111111111111110000// C. NaN// D. Infinity答案是B。这是IEEE 754规范的黑锅,不是JavaScript的题目。表现这么大的数占用过多位数,会丢失精度,学过盘算机构成原理的应该知道是怎么回事。
21. 反转天下

var x = [].reverse;x();// A. []// B. undefined// C. error// D. window答案是D。MDN规范关于 reverse 的形貌:
reverse 方法颠倒数组中元素的位置,并返回该数组的引用。
而这里调用的时间没有订定命组,以是默认的 this 就是 window,以是末了效果返回的是 window。
22. 最小的正值

Number.MIN_VALUE > 0// A. false// B. true// C. error// D. other答案是B。看规范形貌吧:
MIN_VALUE属性是 JavaScript 里最靠近 0 的正值,而不是最小的负值。
MIN_VALUE的值约为 5e-324。小于 MIN_VALUE
("underflow values") 的值将会转换为 0。
由于 MIN_VALUE是 Number 的一个静态属性,因此应该直接使用:Number.MIN_VALUE,而不是作为一个创建的 Number实例的属性。
23. 服膺优先级

[1 < 2 < 3, 3 < 2 < 1]// A. [true, true]// B. [true, false]// C. error// D. other答案是A。<和>的优先级都是从左到右,以是 1 < 2 < 3 会先比力 1 < 2,这会得到 true,但是 < 要求比力的两边都是数字,以是会发生隐式逼迫转换,将 true 转换成 1,以是末了就酿成了比力 1 < 3,效果显然为 true。同理可以分析后者。
24. 坑爹中的战斗机

// the most classic wtf2 == [[[2]]]// A. true// B. false// C. undefined// D. other答案是A。根据ES5规范,假如比力的两个值中有一个是数字范例,就会实验将别的一个值逼迫转换成数字,再举行比力。而数组逼迫转换成数字的过程会先调用它的 toString方法转成字符串,然后再转成数字。以是 [2]会被转成 "2",然后递归调用,终极 [[[2]]] 会被转成数字 2。
25. 小数点把戏

3.toString();3..toString();3...toString();// A. "3", error, error// B. "3", "3.0", error// C. error, "3", error// D. other答案是C。点运算符会被优先辨认为数字常量的一部分,然后才是对象属性访问符。以是 3.toString() 实际上被JS引擎剖析成 (3.)toString(),显然会出现语法错误。但是假如你这么写 (3).toString(),人为加上括号,这就是正当的。
26. 主动提升为全局变量

(function() {  var x = y = 1;})();console.log(y);console.log(x);// A. 1, 1// B. error, error// C. 1, error// D. other答案是C。很经典的例子,在函数中没有效 var 声明变量 y,以是 y 会被主动创建在全局变量 window下面,以是在函数表面也可以访问得到。而 x 由于被 var 声明过,以是在函数外部是无法访问的。
27. 正则表达式实例

var a = /123/;var b = /123/;a == b;a === b;// A. true, true// B. true, false// C. false, false// D. other答案是C。每个字面的正则表达式都是一个单独的实例,纵然它们的内容相同。
28. 数组也爱比巨细

var a = [1, 2, 3];var b = [1, 2, 3];var c = [1, 2, 4];a == b;a === b;a > c;a < c;// A. false, false, false, true// B. false, false, false, false// C. true, true, false, true// D. other答案是A。数组也是对象,ES5规范指出假如两个对象举行相称比力,只有在它们指向同一个对象的情况下才会返回 true,其他情况都返回 false。而对象举行巨细比力,会调用 toString 方法转成字符串举行比力,以是效果就酿成了字符串 "1,2,3" 和 "1,2,4" 按照字典序举行比力了(你若不信,可以重现两个变量的 toString 方法,举行测试)。
29. 原型把戏

var a = {};var b = Object.prototype;[a.prototype === b, Object.getPrototypeOf(a) == b]// A. [false, true]// B. [true, true]// C. [false, false]// D. other答案是A。对象是没有 prototype 属性的,以是 a.prototype 是 undefined,但我们可以通过 Object.getPrototypeOf 方法来获取一个对象的原型。
30. 构造函数的函数

function f() {}var a = f.prototype;var b = Object.getPrototypeOf(f);a === b;// A. true// B. false// C. null// D. other答案是B。这个表明起来有点绕口,我们先来看别的一段代码:
function Person() {}var p = new Person();var a = p.__proto__;var b = Object.getPrototypeOf(p);var c = Person.prototype;console.log(a === b, a === c, b === c);// true, true, truevar d = Person.__proto__;var e = Object.getPrototypeOf(Person);var f = Function.prototype;console.log(d === e, d === f, e === f);// true, true, true起首你要明白,任何函数都是 Function 的实例,而p是函数 Person 的实例,Object.getPrototypeOf 会获取构造当前对象的原型。
以是 Object.getPrototypeOf(p) === Person.prototype,而 Object.getPrototypeOf(Person) === Function.prototype,以是答案就很显着了。我表明的不是很好,假如读者有更好的表明,接待批评。
31. 克制修改函数名

function foo() {}var oldName = foo.name;foo.name = "bar";[oldName, foo.name];// A. error// B. ["", ""]// C. ["foo", "foo"]// D. ["foo", "bar"]答案是C。函数名是克制修改的,规范写的很清楚,以是这里的修改无效。
32. 更换陷阱

"1 2 3".replace(/\d/g, parseInt);// A. "1 2 3"// B. "0 1 2"// C. "NaN 2 3"// D. "1 NaN 3"答案是D。假如 replace 方法第二个参数是一个函数,则会在匹配的时间多次调用,第一个参数是匹配的字符串,第二个参数是匹配字符串的下标。以是酿成了调用 parseInt(1, 0)、parseInt(2, 2)和parseInt(3, 4),效果你就懂了。
33. Function的名字

function f() {}var parent = Object.getPrototypeOf(f);console.log(f.name);console.log(parent.name);console.log(typeof eval(f.name));console.log(typeof eval(parent.name));// A. "f", "Empty", "function", "function"// B. "f", undefined, "function", error// C. "f", "Empty", "function", error// D. other答案是C。根据第30题的表明,我们知道代码中的 parent 实际上就是 Function.prototype,而它在控制台中输出为:
function () {  [native code]}它的 name 属性是 "",以是你 eval("")是得不到任何东西的。
34. 正则测试陷阱

var lowerCaseOnly = /^[a-z]+$/;[lowerCaseOnly.test(null), lowerCaseOnly.test()]// A. [true, false]// B. error// C. [true, true]// D. [false, true]答案是C。test 方法的参数假如不是字符串,会经过抽象 ToString使用逼迫转成字符串,因此实际上测试的是字符串 "null" 和 "undefined"。
35. 逗号界说数组

[,,,].join(", ")// A. ", , , "// B. "undefined, undefined, undefined, undefined"// C. ", , "// D. ""答案是C。JavaScript答应用逗号来界说数组,得到的数组是含有3个 undefined 值的数组。MDN关于 join 方法的形貌:
所有的数组元素被转换成字符串,再用一个分隔符将这些字符勾通接起来。假如元素是undefined 大概null, 则会转化成空字符串。
36. 生存字 class

var a = {class: "Animal", name: "Fido"};console.log(a.class);// A. "Animal"// B. Object// C. an error// D. other答案是D。实际上真正的答案取决于欣赏器。class 是生存字,但是在Chrome、Firefox和Opera中可以作为属性名称,在IE中是克制的。另一方面,实在所有欣赏器根本担当大部分的关键字(如:int、private、throws等)作为变量名,而class是克制的。
37. 无效日期

var a = new Date("epoch");// A. Thu Jan 01 1970 01:00:00 GMT+0100(CET)// B. current time// C. error// D. other答案是D。实际效果是 Invalid Date,它实际上是一个Date对象,由于 a instance Date 的效果是 true,但是它是无效的Date。Date对象内部是用一个数字来存储时间的,在这个例子中,这个数字是 NaN。
38. 神鬼莫测的函数长度

var a = Function.length;var b = new Function().length;console.log(a === b);// A. true// B. false// C. error// D. other答案是B。实际上a的值是1,b的值是0。还是继承来看MDN文档关于 Function.length 的形貌吧!Function构造器的属性:
Function 构造器自己也是个Function。他的 length 属性值为 1 。该属性 Writable: false, Enumerable: false, Configurable: true。
Function原型对象的属性:
Function原型对象的 length 属性值为 0 。
以是,在本例中,a代表的是 Function 构造器的 length 属性,而b代表的是 Function 原型的 length 属性。
39. Date的面具

var a = Date(0);var b = new Date(0);var c = new Date();[a === b, b === c, a === c];// A. [true, true, true]// B. [false, false, false]// C. [false, true, false]// D. [true, false, false]答案是B。先看MDN关于Date对象的留意点:
须要留意的是只能通过调用 Date 构造函数来实例化日期对象:以通例函数调用它(即不加 new 使用符)将会返回一个字符串,而不是一个日期对象。别的,不像其他JavaScript 范例,Date 对象没有字面量格式。
以是a是字符串,b和c是Date对象,而且b代表的是1970年谁人初始化时间,而c代表的是当前时间。
40. min与max共舞

var min = Math.min();var max = Math.max();console.log(min < max);// A. true// B. false// C. error// D. other答案是B。看MDN文档,对 Math.min的形貌:
假如没有参数,效果为Infinity。
对 Math.max 的形貌:
假如没有参数,效果为-Infinity。
41. 鉴戒全局匹配

function captureOne(re, str) {  var match = re.exec(str);  return match && match[1];}var numRe = /num=(\d+)/ig,      wordRe = /word=(\w+)/i,      a1 = captureOne(numRe, "num=1"),      a2 = captureOne(wordRe, "word=1"),      a3 = captureOne(numRe, "NUM=1"),      a4 = captureOne(wordRe, "WORD=1");[a1 === a2, a3 === a4]// A. [true, true]// B. [false, false]// C. [true, false]// D. [false, true]答案是C。看MDN关于 exec 方法的形貌:
当正则表达式使用 "g" 标志时,可以多次实验 exec 方法来查找同一个字符串中的乐成匹配。当你如许做时,查找将从正则表达式的  lastIndex 属性指定的位置开始。
以是a3的值为 null。
42. 最熟悉的生疏人

var a = new Date("2014-03-19");var b = new Date(2014, 03, 19);[a.getDay() == b.getDay(), a.getMonth() == b.getMonth()]// A. [true, true]// B. [true, false]// C. [false, true]// D. [false, false]答案是D。先看MDN关于Date的一个留意事项:
当Date作为构造函数调用并传入多个参数时,假如数值大于公道范围时(如月份为13大概分钟数为70),相邻的数值会被调解。比如 new Date(2013, 13, 1)即是new Date(2014, 1, 1),它们都表现日期2014-02-01(留意月份是从0开始的)。其他数值也是雷同,new Date(2013, 2, 1, 0, 70)即是new Date(2013, 2, 1, 1, 10),都表现时间2013-03-01T01:10:00。
别的,getDay 返回指定日期对象的星期中的第几天(0~6),以是,你懂的。
43. 匹配隐式转换

if("http://giftwrapped.com/picture.jpg".match(".gif")) {  console.log("a gif file");} else {  console.log("not a gif file");}// A. "a gif file"// B. "not a gif file"// C. error// D. other答案是A。看MDN对 match 方法的形貌:
假如传入一个非正则表达式对象,则会隐式地使用 new RegExp(obj)
将其转换为正则表达式对象。
以是我们的字符串 ".gif" 会被转换成正则对象 /.gif/,会匹配到 "/gif"。
44. 重复声明变量

function foo(a) {  var a;  return a;}function bar(a) {  var a = "bye";  return a;}[foo("hello"), bar("hello")]// A. ["hello", "hello"]// B. ["hello", "bye"]// C. ["bye", "bye"]// D. other答案是B。一个变量在同一作用域中已经声明过,会主动移除 var 声明,但是赋值使用仍旧生存,联合前面提到的变量提升机制,你就明白了。
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2025-4-19 19:13, Processed in 0.131163 second(s), 33 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表