44 道 JS ‘难‘题,做对一半就是高手_function f() {} var parent = object

最后

推荐一些系统学习的途径和方法。

路线图

每个Web开发人员必备,很权威很齐全的Web开发文档。作为学习辞典使用,可以查询到每个概念、方法、属性的详细解释,注意使用英文关键字搜索。里面的一些 HTML,CSS,HTTP 技术教程也相当不错。

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

HTML 和 CSS:

html5知识

css基础知识

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。


参考资料:


* ES5规范:11.9.3
* 《你不知道的JavaScript-中卷》


## **15. 撒旦之子“==”**



[]==[]
// A. true// B. false// C. error// D. other


答案是B。ES5规范11.9.3.1-f指出:如果比较的两个对象指向的是同一个对象,就返回 true,否则就返回 false,显然,这是两个不同的数组对象。


参考资料:


* ES5规范:11.9.3.1
* 《你不知道的JavaScript-中卷》


## **16. 加号 VS 减号**



5 + 3; 5 - 3;
// A. “53”, 2// B. 8, 2// C. error// D. other


答案是A。"5" + 2 = "52" 很好理解,+ 运算符中只要有一个是字符串,就会变成字符串拼接操作。你不知道的是,- 运算符要求两个操作数都是数字,如果不是,会强制转换成数字,所以结果就变成了 5 - 2 = 3。


参考资料:


* 《你不知道的JavaScript-中卷》,第四章:4.4.2 字符串和数字之间的隐式强制类型转换


## **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 删除的索引则不会被调用。
> 
> 
> 


参考资料:


* MDN: Array.prototype.map()


## **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的问题。表示这么大的数占用过多位数,会丢失精度,学过计算机组成原理的应该知道是怎么回事。


参考资料:


* Wiki:Double-precision floating-point format


## **21. 反转世界**



var x = [].reverse;x();
// A. []// B. undefined// C. error// D. window


答案是D。MDN规范关于 reverse 的描述:



> 
> reverse 方法颠倒数组中元素的位置,并返回该数组的引用。
> 
> 
> 


而这里调用的时候没有制定数组,所以默认的 this 就是 window,所以最后结果返回的是 window。


参考资料:


* MDN:Array.prototype.reverse()


## **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实例的属性。
> 
> 
> 


参考资料:


* MDN:Number.MIN\_VALUE


## **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。同理可以分析后者。


参考资料:


* MDN:运算符优先级


## **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§;var c = Person.prototype;console.log(a === b, a === c, b === c);// true, true, true
var 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。函数名是禁止修改的,规范写的很清楚,所以这里的修改无效。


参考资料:


* MDN:Function.name


## **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),结果你就懂了。


参考资料:


* MDN:String.prototype.replace()


## **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 = /1+$/;[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, 则会转化成空字符串。
> 
> 
> 


参考资料:


* MDN:Array.prototype.join()


## **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 属性。


参考资料:


* MDN: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代表的是当前时间。


参考资料:


* MDN:Date


## **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。
> 
> 
> 


参考资料:


* MDN:Math.min
* MDN:Math.max


## **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。


参考资料:




#### 专业技能

一般来说,面试官会根据你的简历内容去提问,但是技术基础还有需要自己去准备分类,形成自己的知识体系的。简单列一下我自己遇到的一些题

* HTML+CSS
* JavaScript
* 前端框架
* 前端性能优化
* 前端监控
* 模块化+项目构建
* 代码管理
* 信息安全
* 网络协议
* 浏览器
* 算法与数据结构
* 团队管理
* **[开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】](https://bbs.youkuaiyun.com/forums/4304bb5a486d4c3ab8389e65ecb71ac0)**

最近得空把之前遇到的面试题做了一个整理,包括我本人自己去面试遇到的,还有其他人员去面试遇到的,还有网上刷到的,我都统一的整理了一下,希望对大家有用。



**其中包含HTML、CSS、JavaScript、服务端与网络、Vue、浏览器等等**

**由于文章篇幅有限,仅展示部分内容**

![](https://img-blog.csdnimg.cn/img_convert/ac0b1c2376da47d727e0dc8a77e76478.png)


  1. a-z ↩︎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值