送你58道JavaScript面试题(上)

导读

之前翻译过前阵子 github 很火的 javascript-questions送你43道JavaScript面试题

前两天去看了看已经更新到101题了,这些题目中已经有很多小伙伴贡献了中文翻译,我利用空闲时间也把剩余题目翻译完成并提交了 PullRequest

下面我们来看看都更新了哪些题目吧(本文由原作者授权翻译)!

如果你还没看过前面的43题,请点击这里: 送你43道JavaScript面试题 。我们直接从44题开始:

44. 下面代码的输出是什么?

  • A:  [0,10],[10,20]

  • B:  20,20

  • C:  10,20

  • D:  0,10and10,20

答案: C

一般的函数在执行之后是不能中途停下的。但是,生成器函数却可以中途“停下”,之后可以再从停下的地方继续。当生成器遇到 yield 关键字的时候,会生成 yield 后面的值。注意,生成器在这种情况下不 返回 ( return )值,而是 生成 ( yield )值。

首先,我们用 10 作为参数 i 来初始化生成器函数。然后使用 next() 方法一步步执行生成器。第一次执行生成器的时候, i 的值为 10 ,遇到第一个 yield 关键字,它要生成 i 的值。此时,生成器“暂停”,生成了 10

然后,我们再执行 next() 方法。生成器会从刚才暂停的地方继续,这个时候 i 还是 10 。于是我们走到了第二个 yield 关键字处,这时候需要生成的值是 i*2i10 ,那么此时生成的值便是 20 。所以这道题的最终结果是 10,20

45. 下面代码的返回值是什么?

  • A:  "one"

  • B:  "two"

  • C:  "two""one"

  • D:  "one""two"

答案: B

当我们向 Promise.race 方法中传入多个 Promise 时,会进行 优先 解析。在这个例子中,我们用 setTimeoutfirstPromisesecondPromise 分别设定了500ms和100ms的定时器。这意味着 secondPromise 会首先解析出字符串 two 。那么此时 res 参数即为 two ,是为输出结果。

46. 下面代码的输出是什么?

  • A:  null

  • B:  [null]

  • C:  [{}]

  • D:  [{name:"Lydia"}]

答案: D

首先我们声明了一个拥有 name 属性的对象 person

然后我们又声明了一个变量 members . 将首个元素赋值为变量 person 。 当设置两个对象彼此相等时,它们会通过 引用 进行交互。但是当你将引用从一个变量分配至另一个变量时,其实只是执行了一个 复制 操作。(注意一点,他们的引用 并不相同 !)

接下来我们让 person 等于 null

我们没有修改数组第一个元素的值,而只是修改了变量 person 的值,因为元素(复制而来)的引用与 person 不同。 members 的第一个元素仍然保持着对原始对象的引用。当我们输出 members 数组时,第一个元素会将引用的对象打印出来。

47. 下面代码的输出是什么?

  • A:  {name:"Lydia"},{age:21}

  • B:  "name","age"

  • C:  "Lydia",21

  • D:  ["name","Lydia"],["age",21]

答案: B

for-in 循环中,我们可以通过对象的key来进行迭代,也就是这里的 nameage 。在底层,对象的key都是字符串(如果他们不是Symbol的话)。在每次循环中,我们将 item 设定为当前遍历到的key.所以一开始, itemname ,之后 item 输出的则是 age

48. 下面代码的输出是什么?

  • A:  "345"

  • B:  "75"

  • C:  12

  • D:  "12"

答案: B

当所有运算符的 优先级 相同时,计算表达式需要确定运算符的结合顺序,即从右到左还是从左往右。在这个例子中,我们只有一类运算符 + ,对于加法来说,结合顺序就是从左到右。

3+4 首先计算,得到数字 7 .

由于类型的强制转换, 7+'5' 的结果是 "75" . JavaScript将 7 转换成了字符串,可以参考问题15.我们可以用 + 号把两个字符串连接起来。 "7"+"5" 就得到了 "75" .

49. num 的值是什么?

  • A:  42

  • B:  "42"

  • C:  7

  • D:  NaN

答案: C

只返回了字符串中第一个字母. 设定了 进制 后 (也就是第二个参数,指定需要解析的数字是什么进制: 十进制、十六机制、八进制、二进制等等……), parseInt 检查字符串中的字符是否合法. 一旦遇到一个在指定进制中不合法的字符后,立即停止解析并且忽略后面所有的字符。

* 就是不合法的数字字符。所以只解析到 "7" ,并将其解析为十进制的 7 . num 的值即为 7 .

50. 下面代码的输出是什么?

  • A:  []

  • B:  [null,null,null]

  • C:  [undefined,undefined,undefined]

  • D:  [3x empty]

答案: C

对数组进行映射的时候, num 就是当前循环到的元素. 在这个例子中,所有的映射都是number类型,所以if中的判断 typeofnum==="number" 结果都是 true .map函数创建了新数组并且将函数的返回值插入数组。

但是,没有任何值返回。当函数没有返回任何值时,即默认返回 undefined .对数组中的每一个元素来说,函数块都得到了这个返回值,所以结果中每一个元素都是 undefined .

51. 下面代码输出的是什么?

  • A:  {name:"Lydia"},"1997"

  • B:  {name:"Sarah"},"1998"

  • C:  {name:"Lydia"},"1998"

  • D:  {name:"Sarah"},"1997"

答案: A

普通参数都是 传递的,而对象则不同,是 引用 传递。所以说, birthYear 是值传递,因为他是个字符串而不是对象。当我们对参数进行值传递时,会创建一份该值的 复制 。(可以参考问题46)

变量 birthYear 有一个对 "1997" 的引用,而传入的参数也有一个对 "1997" 的引用,但二者的引用并不相同。当我们通过给 year 赋值 "1998" 来更新 year 的值的时候我们只是更新了 year (的引用)。此时 birthYear 仍然是 "1997" .

person 是个对象。参数 member 引用与之 相同的 对象。当我们修改 member 所引用对象的属性时, person 的相应属性也被修改了,因为他们引用了相同的对象. personname 属性也变成了 "Lydia" .

52. 下面代码的输出是什么?

  • A:  "It worked! Hello world!"

  • B:  "Oh no an error: undefined

  • C:  SyntaxError:can onlythrowErrorobjects

  • D:  "Oh no an error: Hello world!

答案: D

通过 throw 语句,我么可以创建自定义错误。而通过它,我们可以抛出异常。异常可以是一个 字符串 , 一个 数字 , 一个 布尔类型 或者是一个 对象 。在本例中,我们的异常是字符串 'Hello world' .

通过 catch 语句,我们可以设定当 try 语句块中抛出异常后应该做什么处理。在本例中抛出的异常是字符串 'Hello world' . e 就是这个字符串,因此被输出。最终结果就是 'Oh an error: Hello world' .

53. 下面代码的输出是什么?

  • A:  "Lamborghini"

  • B:  "Maserati"

  • C:  ReferenceError

  • D:  TypeError

答案: B

返回属性的时候,属性的值等于 返回的 值,而不是构造函数中设定的值。我们返回了字符串 "Maserati" ,所以 myCar.make 等于 "Maserati" .

54. 下面代码的输出是什么?

  • A:  "undefined","number"

  • B:  "number","number"

  • C:  "object","number"

  • D:  "number","undefined"

答案: A

letx=y=10; 是下面这个表达式的缩写:

我们设定 y 等于 10 时,我们实际上增加了一个属性 y 给全局对象(浏览器里的 window , Nodejs里的 global )。在浏览器中, window.y 等于 10 .

然后我们声明了变量 x 等于 y ,也是 10 .但变量是使用 let 声明的,它只作用于 块级作用域 , 仅在声明它的块中有效;就是案例中的立即调用表达式(IIFE)。使用 typeof 操作符时, 操作值 x 没有被定义:因为我们在 x 声明块的外部,无法调用它。这就意味着 x 未定义。未分配或是未声明的变量类型为 "undefined" . console.log(typeofx) 返回 "undefined" .

而我们创建了全局变量 y ,并且设定 y 等于 10 .这个值在我们的代码各处都访问的到。 y 已经被定义了,而且有一个 "number" 类型的值。 console.log(typeofy) 返回 "number" .

55. 下面代码的输出是什么?

  • A:  "Woof I am Mara"TypeError

  • B:  "Woof I am Mara""Woof I am Mara"

  • C:  "Woof I am Mara"undefined

  • D:  TypeErrorTypeError

答案: A

我们可以用 delete 关键字删除对象的属性,对原型也是适用的。删除了原型的属性后,该属性在原型链上就不可用了。在本例中,函数 bark 在执行了 deleteDog.prototype.bark 后不可用, 然而后面的代码还在调用它。

当我们尝试调用一个不存在的函数时 TypeError 异常会被抛出。在本例中就是 TypeError:pet.barkisnotafunction ,因为 pet.barkundefined .

56. 下面代码的输出是什么?

  • A:  [1,1,2,3,4]

  • B:  [1,2,3,4]

  • C:  {1,1,2,3,4}

  • D:  {1,2,3,4}

答案: D

Set 对象手机 独一无二 的值:也就是说同一个值在其中仅出现一次。

我们传入了数组 [1,1,2,3,4] ,他有一个重复值 1 .以为一个集合里不能有两个重复的值,其中一个就被移除了。所以结果是 {1,2,3,4} .

57. 下面代码的输出是什么?

  • A:  10

  • B:  11

  • C:  Error

  • D:  NaN

答案: C

引入的模块是 只读 的: 你不能修改引入的模块。只有导出他们的模块才能修改其值。

当我们给 myCounter 增加一个值的时候会抛出一个异常: myCounter 是只读的,不能被修改。

58. 下面代码的输出是什么?

  • A:  falsetrue

  • B:  "Lydia"21

  • C:  truetrue

  • D:  undefinedundefined

答案: A

delete 操作符返回一个布尔值: true 指删除成功,否则返回 false . 但是通过 var , constlet 关键字声明的变量无法用 delete 操作符来删除。

name 变量由 const 关键字声明,所以删除不成功:返回 false . 而我们设定 age 等于 21 时,我们实际上添加了一个名为 age 的属性给全局对象。对象中的属性是可以删除的,全局对象也是如此,所以 deleteage 返回 true .

59. 下面代码的输出是什么?

  • A:  [[1,2,3,4,5]]

  • B:  [1,2,3,4,5]

  • C:  1

  • D:  [1]

答案: C

我们可以通过解构赋值来解析来自对象的数组或属性的值,比如说:

a 的值现在是 1b 的值现在是 2 .而在题目中,我们是这么做的:

也就是说, y 等于数组的第一个值就是数字 1 .我们输出 y , 返回 1 .

60. 下面代码的输出是什么?

  • A:  {admin:true,user:{name:"Lydia",age:21}}

  • B:  {admin:true,name:"Lydia",age:21}

  • C:  {admin:true,user:["Lydia",21]}

  • D:  {admin:true}

答案: B

扩展运算符 ... 为对象的组合提供了可能。你可以复制对象中的键值对,然后把它们加到另一个对象里去。在本例中,我们复制了 user 对象键值对,然后把它们加入到 admin 对象中。 admin 对象就拥有了这些键值对,所以结果为 {admin:true,name:"Lydia",age:21} .

61. 下面代码的输出是什么?

  • A:  {name:"Lydia",age:21}["name","age"]

  • B:  {name:"Lydia",age:21}["name"]

  • C:  {name:"Lydia"}["name","age"]

  • D:  {name:"Lydia"}["age"]

答案: B

通过 defineProperty 方法,我们可以给对象添加一个新属性,或者修改已经存在的属性。而我们使用 defineProperty 方法给对象添加了一个属性之后,属性默认为 不可枚举(not enumerable) . Object.keys 方法仅返回对象中 可枚举(enumerable) 的属性,因此只剩下了 "name" .

defineProperty 方法添加的属性默认不可变。你可以通过 writable , configurableenumerable 属性来改变这一行为。这样的话, 相比于自己添加的属性, defineProperty 方法添加的属性有了更多的控制权。

62. 下面代码的输出是什么?

  • A:  "{"level":19, "health":90}"

  • B:  "{"username": "lydiahallie"}"

  • C:  "["level", "health"]"

  • D:  "{"username": "lydiahallie", "level":19, "health":90}"

答案: A

JSON.stringify 的第二个参数是 替代者(replacer) . 替代者(replacer)可以是个函数或数组,用以控制哪些值如何被转换为字符串。

如果替代者(replacer)是个 数组 ,那么就只有包含在数组中的属性将会被转化为字符串。在本例中,只有名为 "level""health" 的属性被包括进来, "username" 则被排除在外。 data 就等于 "{"level":19, "health":90}" .

而如果替代者(replacer)是个 函数 ,这个函数将被对象的每个属性都调用一遍。函数返回的值会成为这个属性的值,最终体现在转化后的JSON字符串中(译者注:Chrome下,经过实验,如果所有属性均返回同一个值的时候有异常,会直接将返回值作为结果输出而不会输出JSON字符串),而如果返回值为 undefined ,则该属性会被排除在外。

63. 下面代码的输出是什么?

  • A:  1010

  • B:  1011

  • C:  1111

  • D:  1112

答案: A

一元操作符 ++ 先返回 操作值, 再累加 操作值。 num1 的值是 10 , 因为 increaseNumber 函数首先返回 num 的值,也就是 10 ,随后再进行 num 的累加。

num210 因为我们将 num1 传入 increasePassedNumber . number 等于 10num1 的值。同样道理, ++ 先返回 操作值, 再累加 操作值。) number10 ,所以 num2 也是 10 .

64. 下面代码输出什么?

  • A:  204080160

  • B:  20402040

  • C:  20202040

  • D:  NaNNaN2040

答案: C

在ES6中,我们可以使用默认值初始化参数。如果没有给函数传参,或者传的参值为 "undefined" ,那么参数的值将是默认值。上述例子中,我们将 value 对象进行了解构并传到一个新对象中,因此 x 的默认值为 {number10}

默认参数在调用时才会进行计算,每次调用函数时,都会创建一个新的对象。我们前两次调用 multiply 函数且不传递值,那么每一次 x 的默认值都为 {number10} ,因此打印出该数字的乘积值为 20

第三次调用 multiply 时,我们传递了一个参数,即对象 value*= 运算符实际上是 x.number=x.number*2 的简写,我们修改了 x.number 的值,并打印出值 20

第四次,我们再次传递 value 对象。 x.number 之前被修改为 20 ,所以 x.number*=2 打印为 40

65. 下面代码输出什么?

  • A:  1 2 and  3 3 and  6 4

  • B:  1 2 and  2 3 and  3 4

  • C:  1 undefined and  2 undefined and  3 undefined and  4 undefined

  • D:  1 2 and  undefined 3 and  undefined 4

答案: D

reducer 函数接收4个参数:

  1. Accumulator (acc) (累计器)

  2. Current Value (cur) (当前值)

  3. Current Index (idx) (当前索引)

  4. Source Array (src) (源数组)

reducer 函数的返回值将会分配给累计器,该返回值在数组的每个迭代中被记住,并最后成为最终的单个结果值。

reducer 函数还有一个可选参数 initialValue , 该参数将作为第一次调用回调函数时的第一个参数的值。如果没有提供 initialValue ,则将使用数组中的第一个元素。

在上述例子, reduce 方法接收的第一个参数(Accumulator)是 x , 第二个参数(Current Value)是 y

在第一次调用时,累加器 x1 ,当前值 y2 ,打印出累加器和当前值: 12

例子中我们的回调函数没有返回任何值,只是打印累加器的值和当前值。如果函数没有返回值,则默认返回 undefined 。在下一次调用时,累加器为 undefined ,当前值为“3”, 因此 undefined3 被打印出。

在第四次调用时,回调函数依然没有返回值。累加器再次为 undefined ,当前值为“4”。 undefined4 被打印出。

66. 使用哪个构造函数可以成功继承 Dog 类?

  • A: 1

  • B: 2

  • C: 3

  • D: 4

答案: B

在子类中,在调用 super 之前不能访问到 this 关键字。如果这样做,它将抛出一个 ReferenceError :1和4将引发一个引用错误。

使用 super 关键字,需要用给定的参数来调用父类的构造函数。父类的构造函数接收 name 参数,因此我们需要将 name 传递给 super

Labrador 类接收两个参数, name 参数是由于它继承了 Dogsize 作为 Labrador 类的额外属性,它们都需要传递给 Labrador 的构造函数,因此使用构造函数2正确完成。

67. 下面代码输出什么?

  • A:  running index.jsrunning sum.js3

  • B:  running sum.jsrunning index.js3

  • C:  running sum.js3running index.js

  • D:  running index.jsundefinedrunning sum.js

答案: B

import 命令是编译阶段执行的,在代码运行之前。因此这意味着被导入的模块会先运行,而导入模块的文件会后执行。

这是CommonJS中 require()import 之间的区别。使用 require() ,您可以在运行代码时根据需要加载依赖项。如果我们使用 require 而不是 importrunning index.jsrunning sum.js3 会被依次打印。

68. 下面代码输出什么?

  • A:  truetruefalse

  • B:  falsetruefalse

  • C:  truefalsetrue

  • D:  truetruetrue

答案: A

每个 Symbol 都是完全唯一的。传递给 Symbol 的参数只是给 Symbol 的一个描述。 Symbol 的值不依赖于传递的参数。当我们测试相等时,我们创建了两个全新的符号:第一个 Symbol'foo' ,第二个 Symbol'foo' , 这两个值是唯一的,彼此不相等,因此返回 false

69. 下面代码输出什么?

  • A:  "Lydia Hallie""Lydia Hallie"

  • B:  " Lydia Hallie"" Lydia Hallie""[13x whitespace]Lydia Hallie""[2x whitespace]Lydia Hallie" )

  • C:  " Lydia Hallie""Lydia Hallie""[1x whitespace]Lydia Hallie""Lydia Hallie" )

  • D:  "Lydia Hallie""Lyd"

答案: C

使用 padStart 方法,我们可以在字符串的开头添加填充。传递给此方法的参数是字符串的总长度(包含填充)。字符串 LydiaHallie 的长度为 12 , 因此 name.padStart13 在字符串的开头只会插入1( 13-12=1 )个空格。

如果传递给 padStart 方法的参数小于字符串的长度,则不会添加填充。

70. 下面代码输出什么?

  • A:  " :computer:"

  • B:  257548

  • C: A string containing their code points

  • D: Error

答案: A

使用 + 运算符,您可以连接字符串。上述情况,我们将字符串 “ ” 与字符串 ”:computer:“ 连接起来,产生 ” :computer:“

71. 如何能打印出 console.log 语句后注释掉的值?

  • A:  game.next("Yes").value and  game.next().value

  • B:  game.next.value("Yes") and  game.next.value()

  • C:  game.next().value and  game.next("Yes").value

  • D:  game.next.value() and  game.next.value("Yes")

答案: C

generator 函数在遇到 yield 关键字时会“暂停”其执行。首先,我们需要让函数产生字符串 Doyou loveJavaScript? ,这可以通过调用 game.next().value 来完成。上述函数的第一行就有一个 yield 关键字,那么运行立即停止了, yield 表达式本身没有返回值,或者说总是返回 undefined , 这意味着此时变量 answerundefined

next 方法可以带一个参数,该参数会被当作上一个 yield 表达式的返回值。当我们调用 game.next("Yes").value 时,先前的 yield 的返回值将被替换为传递给 next() 函数的参数 "Yes" 。此时变量 answer 被赋值为 "Yes"if 语句返回 false ,所以 JavaScriptloves you back:heart: 被打印。

72. 下面代码输出什么?

  • A:  Helloworld!

  • B:  Hello
    world

  • C:  Hello\nworld

  • D:  Hello\n
    world

答案: C

String.raw 函数是用来获取一个模板字符串的原始字符串的,它返回一个字符串,其中忽略了转义符( \n\v\t 等)。但反斜杠可能造成问题,因为你可能会遇到下面这种类似情况:

这将导致:

"C:DocumentsProjects able.html"

直接使用 String.raw

它会忽略转义字符并打印: C:\Documents\Projects\table.html

上述情况,字符串是 Hello\nworld 被打印出。

73.下面代码输出什么?

  • A:  "I made it!"

  • B:  Promise{<resolved>:"I made it!"}

  • C:  Promise{<pending>}

  • D:  undefined

答案: C

异步函数始终返回一个promise。 await 仍然需要等待promise的解决:当我们调用 getData() 并将其赋值给 data ,此时 datagetData 方法返回的一个挂起的promise,该promise并没有解决。

如果我们想要访问已解决的值 "I made it!" ,可以在 data 上使用 .then() 方法:

data.then(res=>console.log(res))

这样将打印 "I made it!"

74. 下面代码输出什么?

  • A:  ['apple','banana']

  • B:  2

  • C:  true

  • D:  undefined

答案: B

push() 方法返回新数组的长度。一开始,数组包含一个元素(字符串 "banana" ),长度为1。 在数组中添加字符串 "apple" 后,长度变为2,并将从 addToList 函数返回。

push 方法修改原始数组,如果你想从函数返回数组而不是数组长度,那么应该在push item 之后返回 list

我来评几句
登录后评论

已发表评论数()

相关站点

热门文章