前端面试题整理,更新中。。。

张开发
2026/4/19 6:55:00 15 分钟阅读

分享文章

前端面试题整理,更新中。。。
1、事件循环Event Loop什么是事件循环事件循环是 JavaScript 实现异步编程的核心机制它负责协调同步代码、微任务、宏任务之间的执行顺序。看这段代码不要运行凭理解写出输出顺序正确答案是1, 4, 3, 2核心原因setTimeout 是宏任务Promise.then 是微任务微任务 宏任务在每次宏任务执行完后会清空所有微任务画出事件循环模型一个更有挑战的例子asyncfunctiontest(){console.log(A)await1console.log(B)}console.log(C)test()console.log(D)// 输出顺序是正确答案是C, A, D, B核心原因await 后面的代码会被包装成微任务所以 console.log(‘B’) 会在微任务阶段执行顺序同步代码(C、A、D) → 微任务(B)知识点补充宏任务有哪些I/O 操作Input/Output输入/输出通俗理解程序与外部设备交换数据的过程UI 渲染*通俗理解浏览器将代码变成用户看到的画面的过程注意1为什么 I/O 是宏任务因为这些操作需要等待外部响应网络、磁盘、用户不能阻塞主线程所以被放入宏任务队列异步执行。2为什么 UI 渲染是宏任务浏览器会把多次 DOM 操作合并成一次渲染避免重复渲染浪费性能。微任务有哪些Promise.then()Promise.catch()Promise.finally()queueMicrotask()MutationObserver记忆口诀宏任务定时、I/O、事件、渲染微任务Promise、queueMicrotask执行顺序同步代码 → 清空微任务 → 取一个宏任务 → 清空微任务 → 可能渲染→ 重复进阶版setTimeout((){console.log(A)setTimeout(()console.log(B),0)// 注意这里是 setTimeout不是 Promise},0)Promise.resolve().then(()console.log(C))console.log(D)正确答案输出是 D C AB 不会在此时输出记忆口诀微任务追着宏任务跑同一轮宏任务排着队等下一轮总结核心原则微任务不排队产生的微任务立即执行在当前宏任务结束前宏任务要排队产生的宏任务下一轮执行不管是谁产生的注意1new Promise 构造函数是同步的2await 让函数内部等待但不让外部等待它会让出控制权让外部代码先执行等外部同步代码执行完再回来执行 await 后面的代码作为微任务。即await 让出控制权后面代码变微任务。同步代码继续跑同步跑完才清微。3微任务队列是 FIFO先进先出但新微任务产生时会追加到队尾。4内层 await 的微任务先产生外层 await 的微任务后产生2、手写 Promise 源码classMyPromise{staticPENDINGpendingstaticFULFILLEDfulfilledstaticREJECTEDrejectedconstructor(executor){this.stateMyPromise.PENDINGthis.valuenullthis.reasonnullthis.onFulfilledCallbacks[]this.onRejectedCallbacks[]constresolve(value){if(this.stateMyPromise.PENDING){// 处理 resolve 一个 Promise 的情况if(valueinstanceofMyPromise){value.then(resolve,reject)return}this.stateMyPromise.FULFILLEDthis.valuevaluethis.onFulfilledCallbacks.forEach(fnfn())}}constreject(reason){if(this.stateMyPromise.PENDING){this.stateMyPromise.REJECTEDthis.reasonreasonthis.onRejectedCallbacks.forEach(fnfn())}}try{executor(resolve,reject)}catch(error){reject(error)}}// Promise 解析过程核心staticresolvePromise(promise,x,resolve,reject){// 2.3.1 防止循环引用if(promisex){reject(newTypeError(Chaining cycle detected))return}// 2.3.2 如果 x 是 MyPromise 实例if(xinstanceofMyPromise){x.then(resolve,reject)return}// 2.3.3 如果 x 是对象或函数if(x!null(typeofxobject||typeofxfunction)){letcalledfalsetry{constthenx.thenif(typeofthenfunction){// 2.3.3.3 将 x 作为 this 调用 thenthen.call(x,(y){if(called)returncalledtrue// 递归解析MyPromise.resolvePromise(promise,y,resolve,reject)},(r){if(called)returncalledtruereject(r)})}else{resolve(x)}}catch(error){if(called)returncalledtruereject(error)}}else{// 2.3.4 普通值直接 resolveresolve(x)}}then(onFulfilled,onRejected){// 2.2.1 参数默认值onFulfilledtypeofonFulfilledfunction?onFulfilled:valuevalue onRejectedtypeofonRejectedfunction?onRejected:reason{throwreason}// 2.2.7 then 必须返回一个 PromiseconstnewPromisenewMyPromise((resolve,reject){// 包装成微任务执行的函数consthandleFulfilled(){runMicroTask((){try{constresultonFulfilled(this.value)MyPromise.resolvePromise(newPromise,result,resolve,reject)}catch(error){reject(error)}})}consthandleRejected(){runMicroTask((){try{constresultonRejected(this.reason)MyPromise.resolvePromise(newPromise,result,resolve,reject)}catch(error){reject(error)}})}// 2.2.4 根据状态执行回调if(this.stateMyPromise.FULFILLED){handleFulfilled()}elseif(this.stateMyPromise.REJECTED){handleRejected()}elseif(this.stateMyPromise.PENDING){this.onFulfilledCallbacks.push(handleFulfilled)this.onRejectedCallbacks.push(handleRejected)}})returnnewPromise}catch(onRejected){returnthis.then(null,onRejected)}finally(callback){returnthis.then((value){returnMyPromise.resolve(callback()).then(()value)},(reason){returnMyPromise.resolve(callback()).then((){throwreason})})}// 静态方法staticresolve(value){if(valueinstanceofMyPromise)returnvaluereturnnewMyPromise((resolve)resolve(value))}staticreject(reason){returnnewMyPromise((resolve,reject)reject(reason))}staticall(promises){returnnewMyPromise((resolve,reject){if(!Array.isArray(promises)){reject(newTypeError(Argument must be an array))return}constresults[]letcompleted0if(promises.length0){resolve(results)return}promises.forEach((promise,index){MyPromise.resolve(promise).then((value){results[index]value completedif(completedpromises.length){resolve(results)}},reject)})})}staticrace(promises){returnnewMyPromise((resolve,reject){if(!Array.isArray(promises)){reject(newTypeError(Argument must be an array))return}promises.forEach((promise){MyPromise.resolve(promise).then(resolve,reject)})})}staticany(promises){returnnewMyPromise((resolve,reject){if(!Array.isArray(promises)){reject(newTypeError(Argument must be an array))return}consterrors[]letrejectedCount0if(promises.length0){reject(newAggregateError(errors,All promises were rejected))return}promises.forEach((promise,index){MyPromise.resolve(promise).then(resolve,(error){errors[index]error rejectedCountif(rejectedCountpromises.length){reject(newAggregateError(errors,All promises were rejected))}})})})}staticallSettled(promises){returnnewMyPromise((resolve){if(!Array.isArray(promises)){resolve([])return}constresults[]letcompleted0if(promises.length0){resolve(results)return}promises.forEach((promise,index){MyPromise.resolve(promise).then((value){results[index]{status:fulfilled,value}completedif(completedpromises.length)resolve(results)},(reason){results[index]{status:rejected,reason}completedif(completedpromises.length)resolve(results)})})})}}测试// 测试微任务console.log(1)MyPromise.resolve().then(()console.log(2)).then(()console.log(3))console.log(4)// 输出1, 4, 2, 3// 测试 thenableMyPromise.resolve().then(()({then:(resolve){setTimeout(()resolve(thenable),1000)}})).then(valueconsole.log(value))// 1秒后输出 thenable// 测试循环引用constpMyPromise.resolve().then(()p)p.catch(errconsole.log(err.message))// Chaining cycle detected// 测试 anyMyPromise.any([MyPromise.reject(1),MyPromise.reject(2),MyPromise.resolve(3)]).then(valueconsole.log(value))// 3// 测试 allSettledMyPromise.allSettled([MyPromise.resolve(1),MyPromise.reject(2),MyPromise.resolve(3)]).then(resultsconsole.log(results))// [{status:fulfilled,value:1}, {status:rejected,reason:2}, {status:fulfilled,value:3}]建议 熟练手写基础结构状态、构造函数、resolve/reject、then同步 存储回调、then 返回新 Promise 微任务能说清楚resolvePromise嵌套 Promise 和 thenable的原理就足够应对高级前端面试了面试重点必须能说清楚的点为什么需要 resolvePromise为什么回调要包装成微任务面试常见追问

更多文章