[关闭]
@wy 2019-07-15T19:46:04.000000Z 字数 4794 阅读 507

JavaScript 进阶问题列表,你掌握了多少?

面试题


经常写业务就容易忽视对基础知识的补充和加强,但在面试中,基础知识点是非常重要的考核部分。本文要分享的是,一位开发者每天都会发布的 JavaScript 问题。有的容易,有的会有难度,对基础知识的查缺补漏非常有帮助,也是你进阶路上必然要掌握的知识。

由于篇幅限制,以下挑选了10个问题,紧跟其后的就是对这道题的详细解答。如果你想看所有的题目,可以点击查看原文。

大多都是运行题,如果可以,只在你的最强大脑运行,迅速给出结果,再看答案,看你掌握了多少。

Let's go...

1. for循环中套定时器

  1. for (var i = 0; i < 3; i++) {
  2. setTimeout(() => console.log(i), 1)
  3. }
  4. for (let i = 0; i < 3; i++) {
  5. setTimeout(() => console.log(i), 1)
  6. }

答案: C

由于 JavaScript 的事件循环,setTimeout 回调会在遍历结束后才执行。因为在第一个遍历中遍历 i 是通过 var 关键字声明的,所以这个值是全局作用域下的。在遍历过程中,我们通过一元操作符 ++ 来每次递增 i 的值。当 setTimeout 回调执行的时候,i 的值等于 3。

在第二个遍历中,遍历 i 是通过 let 关键字声明的:通过 letconst 关键字声明的变量是拥有块级作用域(指的是任何在 {} 中的内容)。在每次的遍历过程中,i 都有一个新值,并且每个值都在循环内的作用域中。

2. 标记模板字面量

  1. function getPersonInfo(one, two, three) {
  2. console.log(one)
  3. console.log(two)
  4. console.log(three)
  5. }
  6. const person = 'Lydia'
  7. const age = 21
  8. getPersonInfo`${person} is ${age} years old`

答案: B

如果使用标记模板字面量,第一个参数的值总是包含字符串的数组。其余的参数获取的是传递的表达式的值!

3. typeof 运算符返回值

  1. function sayHi() {
  2. return (() => 0)()
  3. }
  4. typeof sayHi()

答案: B

sayHi 方法返回的是立即执行函数(IIFE)的返回值.此立即执行函数的返回值是 0, 类型是 number

参考:只有7种内置类型:nullundefinedbooleannumberstringobjectsymbolfunction 不是一种类型,函数是对象,它的类型是object

4. 对象作为key值的问题

  1. const a = {}
  2. const b = { key: 'b' }
  3. const c = { key: 'c' }
  4. a[b] = 123
  5. a[c] = 456
  6. console.log(a[b])

答案: B

对象的键被自动转换为字符串。我们试图将一个对象 b 设置为对象 a 的键,且相应的值为 123

然而,当字符串化一个对象时,它会变成 "[object Object]"。因此这里说的是,a["[object Object]"] = 123。然后,我们再一次做了同样的事情,c 是另外一个对象,这里也有隐式字符串化,于是,a["[object Object]"] = 456

然后,我们打印 a[b],也就是 a["[object Object]"]。之前刚设置为 456,因此返回的是 456

5. call和bind的使用

  1. const person = { name: 'Lydia' }
  2. function sayHi(age) {
  3. console.log(`${this.name} is ${age}`)
  4. }
  5. sayHi.call(person, 21)
  6. sayHi.bind(person, 21)

答案: D

使用这两种方法,我们都可以传递我们希望 this 关键字引用的对象。但是,.call立即执行的。

.bind 返回函数的副本,但带有绑定上下文!它不是立即执行的。

6. reduce 方法的使用

  1. ```javascript
  2. [1, 2, 3, 4].reduce((x, y) => console.log(x, y));
  3. <div class="md-section-divider"></div>

答案: 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,当前值“y”2,打印出累加器和当前值:12

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

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

7. Promise.race的用法

  1. const firstPromise = new Promise((res, rej) => {
  2. setTimeout(res, 500, "one");
  3. });
  4. const secondPromise = new Promise((res, rej) => {
  5. setTimeout(res, 100, "two");
  6. });
  7. Promise.race([firstPromise, secondPromise]).then(res => console.log(res));
  8. <div class="md-section-divider"></div>

答案: B

当我们向Promise.race方法中传入多个Promise时,会进行 优先 解析(注:只要有一个成功或失败,就立马结束)。在这个例子中,我们用setTimeoutfirstPromisesecondPromise分别设定了500ms和100ms的定时器。这意味着secondPromise会首先解析出字符串two。那么此时res参数即为two,是为输出结果。

8. try...catch 捕获错误

  1. function greeting() {
  2. throw "Hello world!";
  3. }
  4. function sayHi() {
  5. try {
  6. const data = greeting();
  7. console.log("It worked!", data);
  8. } catch (e) {
  9. console.log("Oh no an error!", e);
  10. }
  11. }
  12. sayHi();
  13. <div class="md-section-divider"></div>

答案: D

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

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

9. import 执行顺序问题

  1. // index.js
  2. console.log('running index.js');
  3. import { sum } from './sum.js';
  4. console.log(sum(1, 2));
  5. // sum.js
  6. console.log('running sum.js');
  7. export const sum = (a, b) => a + b;
  8. <div class="md-section-divider"></div>

答案: B

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

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

10. JSON.stringify 过滤需要的字段

  1. const settings = {
  2. username: "lydiahallie",
  3. level: 19,
  4. health: 90
  5. };
  6. const data = JSON.stringify(settings, ["level", "health"]);
  7. console.log(data);

答案: A

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

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

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

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注