以下是几道高质量的 ES6+ 综合面试题,覆盖变量作用域、闭包、Promise、解构、this、async/await 等知识点,适合中高级前端岗位筛选与自我训练:
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)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
console.log("1")
输出 → 1
声明 asyncFunc
(不会执行,跳过)
调用 asyncFunc()
:
2
await Promise.resolve()
→ 进入暂停状态,并将后面的 console.log("3")
注册为一个微任务注册一个 Promise.resolve().then(...)
→ 将 console.log("4")
注册为另一个微任务
console.log("5")
输出 → 5
Promise.resolve().then(...)
→ 输出 4
await
后面的微任务恢复 → 输出 3
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
的顺序取决于注册先后。
let a = 1;
function test() {
console.log(a);
let a = 2;
}
test();
❓这段代码会输出什么?
✅ 正确答案:
报错:Cannot access 'a' before initialization
原因:
let a = 2
在作用域中形成暂时性死区(TDZ),在此之前访问会报错。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 () {}
的 this
是 window
(非严格模式);obj
。const [a = 1, b = a + 1, c = b + 1] = [undefined, undefined];
console.log(a, b, c);
✅ 输出:
1 2 3
解构中默认值是惰性求值,逐个执行:
Promise.resolve()
.then(() => {
console.log('A');
return Promise.resolve('B');
})
.then((res) => {
console.log(res);
});
✅ 输出:
A
B
链式 then 中 return Promise,会等待该 Promise 解析完成并传递结果到下一个 then。
const sym = Symbol('test');
console.log(typeof sym);
console.log(typeof Symbol);
✅ 输出:
symbol
function
Symbol 是基本类型;但 Symbol
构造器本身是函数。
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"
是怎么变成微任务的async1()
打印 "1"
(同步执行)
遇到 await async2()
:
async2()
,打印 "3"
(同步)await
的行为:暂停函数的后续代码,挂起为微任务这意味着:
await async2();
console.log("2");
这两行中的第二行 console.log("2")
,会被推迟到微任务队列中,在当前宏任务完成后执行。
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
后代码执行。