es6/7练习题1

发布于:2025-05-09 ⋅ 阅读:(14) ⋅ 点赞:(0)


前言

以下是几道高质量的 ES6+ 综合面试题,覆盖变量作用域、闭包、Promise、解构、this、async/await 等知识点,适合中高级前端岗位筛选与自我训练:


题目 1:输出顺序题(async + Promise + 微任务)

console.log("1");

async function asyncFunc() {
  console.log("2");
  await Promise.resolve();
  console.log("3");
}

asyncFunc();

Promise.resolve().then(() => {
  console.log("4");
});

console.log("5");

🧠 你能写出输出顺序吗?为什么?

✅ 正确输出:

1
2
5
4
3

这道题是ES6 面试经典题,考察你对以下内容的理解:

📌 涉及知识点:

  • async/await 的本质
  • Promise 微任务(microtask)
  • JavaScript 的 事件循环(Event Loop)
  • 同步 vs 微任务执行顺序

✅ 原始代码:

console.log("1");

async function asyncFunc() {
  console.log("2");
  await Promise.resolve();
  console.log("3");
}

asyncFunc();

Promise.resolve().then(() => {
  console.log("4");
});

console.log("5");

✅ 输出顺序是什么?

1
2
5
4
3

🧠 步骤详解:

🔷 第一步:执行同步代码

  1. console.log("1") 输出 → 1

  2. 声明 asyncFunc(不会执行,跳过)

  3. 调用 asyncFunc()

    • 输出 2
    • 遇到 await Promise.resolve() → 进入暂停状态,并将后面的 console.log("3") 注册为一个微任务
  4. 注册一个 Promise.resolve().then(...) → 将 console.log("4") 注册为另一个微任务

  5. console.log("5") 输出 → 5

🔷 第二步:执行微任务队列(当前宏任务执行完后)

  1. 执行 Promise.resolve().then(...) → 输出 4
  2. await 后面的微任务恢复 → 输出 3

🧠 async/await 背后发生了什么?

await 会把其后面的语句(如 console.log("3")放入微任务队列,不会立刻执行!

等价于:

function asyncFunc() {
  console.log("2");
  return Promise.resolve().then(() => {
    console.log("3");
  });
}

✅ 最终执行顺序汇总

阶段 语句 输出
同步 console.log("1") 1
同步 asyncFunc() → log(“2”) 2
同同步 console.log("5") 5
微任务队列 .then(() => console.log("4")) 4
微任务队列 await 后 log(“3”) 3

✅ 小结口诀(面试必备)

await 后的代码是微任务,必须等当前同步执行完 + 微任务队列依次执行;多个 .then()await 的顺序取决于注册先后。


题目 2:作用域与解构陷阱题

let a = 1;
function test() {
  console.log(a);
  let a = 2;
}
test();

❓这段代码会输出什么?

✅ 正确答案:

报错:Cannot access 'a' before initialization

🧠 原因:

  • 虽然是函数作用域,但 let a = 2 在作用域中形成暂时性死区(TDZ),在此之前访问会报错。

题目 3:this 指向 + 箭头函数

const obj = {
  name: "vue",
  say() {
    setTimeout(function () {
      console.log("1:", this.name)
    }, 0)

    setTimeout(() => {
      console.log("2:", this.name)
    }, 0)
  }
}

obj.say();

✅ 输出:

1: undefined
2: vue

🧠 原因:

  • 普通函数 function () {}thiswindow(非严格模式);
  • 箭头函数不绑定 this,继承自 obj

题目 4:数组解构与默认值

const [a = 1, b = a + 1, c = b + 1] = [undefined, undefined];
console.log(a, b, c);

✅ 输出:

1 2 3

🧠 解构中默认值是惰性求值,逐个执行:

  • a = 1
  • b = a + 1 = 2
  • c = b + 1 = 3

题目 5:Promise 链式调用陷阱题

Promise.resolve()
  .then(() => {
    console.log('A');
    return Promise.resolve('B');
  })
  .then((res) => {
    console.log(res);
  });

✅ 输出:

A
B

🧠 链式 then 中 return Promise,会等待该 Promise 解析完成并传递结果到下一个 then。


题目 6:typeof 与 Symbol

const sym = Symbol('test');
console.log(typeof sym);
console.log(typeof Symbol);

✅ 输出:

symbol
function

🧠 Symbol 是基本类型;但 Symbol 构造器本身是函数。


✅ 原始代码回顾(题目 3):

async function async1() {
  console.log("1");
  await async2();
  console.log("2");
}

async function async2() {
  console.log("3");
}

console.log("4");

setTimeout(() => {
  console.log("5");
}, 0);

async1();

new Promise((resolve) => {
  console.log("6");
  resolve();
}).then(() => {
  console.log("7");
});

console.log("8");

🧠 我们重点看 "2" 是怎么变成微任务的

🔹 1. 调用 async1()

  • 打印 "1"(同步执行)

  • 遇到 await async2()

    • 执行 async2(),打印 "3"(同步)
    • await 的行为:暂停函数的后续代码,挂起为微任务

这意味着:

await async2();
console.log("2");

这两行中的第二行 console.log("2")会被推迟到微任务队列中,在当前宏任务完成后执行。


🔁 等价的 Promise 写法是什么?

function async1() {
  console.log("1")
  return async2().then(() => {
    console.log("2")
  })
}

✅ 那为什么 "7" 会比 "2" 先打印?

因为这行代码:

new Promise((resolve) => {
  console.log("6")
  resolve()
}).then(() => {
  console.log("7")
});

中:

  • .then(() => { console.log("7") }) 是在当前宏任务中同步创建并注册的微任务
  • console.log("2")async1 函数在 await 之后才挂起注册的微任务

所以注册顺序是:

微任务 1:then(() => console.log("7"))   ← 更早注册
微任务 2:await async2() 后 console.log("2")  ← 稍后注册

✅ 最终执行顺序解释:

阶段 内容 输出
同步任务 4 → 1 → 3 → 6 → 8 4, 1, 3, 6, 8
微任务队列 then → console.log(“7”) 7
微任务队列 await 后 console.log(“2”) 2
宏任务 setTimeout → console.log(“5”) 5

🧠 小结:为什么 "2" 是微任务而 "7" 先执行?

await 会把 await 后的语句包装成微任务,但它的注册是异步的,所以在当前宏任务后期才注册;
.then()同步注册微任务,因此先于 await 后代码执行。



网站公告

今日签到

点亮在社区的每一天
去签到