44道比较难的 JS 面试题,2024年最新字节跳动前端面经社招

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Web前端全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024c (备注前端)
img

正文

  • 《你不知道的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 wtf

2 == [[[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§ === 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。

参考资料:

  • MDN:RegExp.prototype.exec()

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),所以,你懂的。

参考资料:

  • MDN:Date

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”。

参考资料:

  • MDN:String.prototype.match()

44. 重复声明变量

===============

function foo(a) {

var a;

return a;

}

function bar(a) {

var a = “bye”;

return a;

}

[foo(“hello”), bar(“hello”)]

// A. [“hello”, “hello”]

最后:

总结来说,面试成功=基础知识+项目经验+表达技巧+运气。我们无法控制运气,但是我们可以在别的地方花更多时间,每个环节都提前做好准备。

面试一方面是为了找到工作,升职加薪,另一方面也是对于自我能力的考察。能够面试成功不仅仅是来自面试前的临时抱佛脚,更重要的是在平时学习和工作中不断积累和坚持,把每个知识点、每一次项目开发、每次遇到的难点知识,做好积累,实践和总结。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024c (备注前端)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
1, 1, 10),都表示时间2013-03-01T01:10:00。

此外,getDay 返回指定日期对象的星期中的第几天(0~6),所以,你懂的。

参考资料:

  • MDN:Date

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”。

参考资料:

  • MDN:String.prototype.match()

44. 重复声明变量

===============

function foo(a) {

var a;

return a;

}

function bar(a) {

var a = “bye”;

return a;

}

[foo(“hello”), bar(“hello”)]

// A. [“hello”, “hello”]

最后:

总结来说,面试成功=基础知识+项目经验+表达技巧+运气。我们无法控制运气,但是我们可以在别的地方花更多时间,每个环节都提前做好准备。

面试一方面是为了找到工作,升职加薪,另一方面也是对于自我能力的考察。能够面试成功不仅仅是来自面试前的临时抱佛脚,更重要的是在平时学习和工作中不断积累和坚持,把每个知识点、每一次项目开发、每次遇到的难点知识,做好积累,实践和总结。

[外链图片转存中…(img-qCVDWOVY-1713026027532)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024c (备注前端)
[外链图片转存中…(img-keiH4Kis-1713026027533)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!


  1. a-z ↩︎

  • 7
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值