高级前端面试题及答案(最新版)
JavaScript 深入
1. 解释 Event Loop 机制,包括宏任务和微任务的区别
答案: Event Loop 是 JavaScript 实现异步的核心机制,它由以下部分组成:
- 调用栈(Call Stack):同步代码的执行栈
- 任务队列(Task Queue):存放宏任务
- 微任务队列(Microtask Queue):存放微任务
执行顺序:
- 执行同步代码(属于第一个宏任务)
- 执行当前宏任务产生的所有微任务
- 执行下一个宏任务
- 循环...
区别:
| 宏任务(Macrotask) | 微任务(Microtask) | |
|---|---|---|
| 示例 | setTimeout, setInterval, I/O | Promise.then, MutationObserver |
| 执行时机 | Event Loop的每个循环执行一个 | 在每个宏任务结束后立即全部执行 |
| API | Run by host environment | Run by JS engine |
2. Proxy/Reflect API的高级应用场景
答案: Proxy可以用于:
// 1. API请求拦截器
const apiHandler = {
get(target, prop) {
return async (...args) => {
console.log(`Calling ${prop} with`, args);
return target[prop](...args).catch(err => {
//统一错误处理
sentry.captureException(err);
throw err;
});
};
}
};
//2.响应式数据实现(Vue3原理)
function reactive(obj) {
return new Proxy(obj, {
get(target, key) {
track(target, key); //依赖收集
return Reflect.get(...arguments);
},
set(target, key, value) {
trigger(target, key); //触发更新
return Reflect.set(...arguments);
}
});
}
Reflect的用途:
//1.替代Object上的方法,更函数式
const obj = { foo: 'bar' };
Reflect.has(obj, 'foo'); //替代'foo' in obj
//2.与Proxy配合使用保持默认行为
const proxy = new Proxy(obj, {
get(target, prop) {
if(prop === 'secret') return undefined;
return Reflect.get(...arguments); //保持默认行为
}
});
React高级特性
###3.React Fiber架构原理
答案: Fiber是React16引入的新协调引擎,核心改进:
-
可中断渲染:将渲染工作拆分为多个小单元(fiber节点),每个单元完成后检查剩余时间,没有时间则暂停并让出主线程。
-
双缓冲技术:维护两棵fiber树(Current和WorkInProgress),减少直接操作DOM的开销。
-
优先级调度:区分不同优先级的更新(如用户交互>数据获取)。
-
新的生命周期:划分render阶段(pre-commit phase)和commit阶段。
关键数据结构:
interface FiberNode {
tag: WorkTag; //组件类型(Function/Class/Host等)
key: string | null;
elementType: any;
stateNode: any; //对应的实例
//链表结构
return: FiberNode | null; //父节点
child: FiberNode | null; //第一个子节点
sibling: FiberNode | null; //兄弟节点
//更新相关
memoizedState: any; //hooks链表(Hook会挂载到这里)
//副作用标记(EffectTag)
flags: Flags;
}
###4.Hooks实现原理及自定义高级Hook示例
实现原理: 1.React通过维护一个"current dispatcher"变量来区分mount/update阶段。 2.Function组件首次渲染时创建hook链表并挂载到fiber.memoizedState上。 3.Hook对象结构:
type Hook = {
memoizedState: any, //当前状态值(useState)/effect对象(useEffect)
baseState: any,
baseQueue: Update<any>,
queue: UpdateQueue<any>, //待处理的更新队列
next: Hook | null //下一个hook(形成链表)
};
高级Hook示例 - useAsync:
function useAsync(asyncFunction, immediate = true) {
const [status, setStatus] = useState('idle');
const [value, setValue] = useState(null);
const [error, setError] = useState(null);
const execute = useCallback(() =>{
setStatus('pending');
setValue(null);
setError(null);
return asyncFunction()
.then(response =>{
setValue(response);
setStatus('success');
})
.catch(error =>{
setError(error);
setStatus('error');
});
}, [asyncFunction]);
useEffect(() =>{
if (immediate) execute();
}, [execute]);
return { execute, status, value, error };
}
//使用示例:
const { execute }= useAsync(fetchData);
<button onClick={execute}>重新加载</button>
##性能优化专题
###5.Web Worker优化长列表渲染方案
方案代码:
//主线程:
function renderWithWorker(listData){
const worker=new Worker('./listWorker.js');
worker.postMessage({
type:'init',
data:{ items:listData }
});
worker.onmessage=(e)=>{
if(e.data.type==='chunk-ready'){
requestIdleCallback((deadline)=>{
while(deadline.timeRemaining()>0 && e.data.chunk.length){
renderChunk(e.data.chunk.shift());
}
});
}
};
}
//Worker线程(listWorker.js):
self.onmessage=function(e){
if(e.data.type==='init'){
let index=0;
const chunkSize=50;
function processChunk(){
if(index>=e.data.items.length){
self.postMessage({ type:'done' });
return;
}
const chunk=e.data.items.slice(index,
Math.min(index+chunkSize,e.data.items.length));
index+=chunkSize;
self.postMessage({
type:'chunk-ready',
chunk:[chunk]
});
requestAnimationFrame(processChunk);
}
processChunk();
}
};
关键点说明: 1.Worker处理数据分块避免阻塞主线程UI渲染。 2.requestIdleCallback确保只在浏览器空闲时处理UI更新。 3.chunk大小动态调整可基于设备性能指标。
###6.WebAssembly在前端性能优化的实践案例
典型应用场景及对比:
场景一:图像处理(PDF.js中的色彩转换)
传统JS实现:
function convertRGBtoCMYK(pixels){
for(let i=0;i<pixels.length;i+=4){
let r=pixels[i]/255;
let g=pixels[i+1]/255;
let b=pixels[i+2]/255;
let k=1-Math.max(r,g,b);
pixels[i]=k===1?0:(1-r-k)/(1-k)*255;//C
pixels[i+1]=k===1?0:(1-g-k)/(1-k)*255;//M
pixels[i+2]=k===1?0:(1-b-k)/(1-k)*255;//Y
pixels[i+3]=k*255;//K
}
}
WebAssembly版本(Rust):
#[wasm_bindgen]
pub fn convert_rgb_to_cmyk(pixels:&mut[u8]){
for i in (0..pixels.len()).step_by(4){
let r=f64::from(pixels[i])/255.0;
let g=f64::from(pixels[i+1])/255.0;
let b=f64::from(pixels[i+2])/255.0;
let k=1.0-r.max(g).max(b);
pixels[i]=if k==1.0{0}else{((1.0-r-k)/(1.0-k)*255.)as u8};
pixels[i+3]=(k*255.)as u8;
}
}
性能对比指标:
| 指标 | 纯JS实现 | WASM版本 |
|---|---|---|
| 10MB图片处理时间 | 320ms | 85ms |
| CPU占用峰值 | 98% | 45% |
| 内存占用 | 120MB | 65MB |
##TypeScript高级特性
###7.TS类型编程实战:实现Vuex的类型推导
完整类型定义方案:
interface ModuleTree<R>{
[key:string]:Module<R>;
}
interface Module<S,R={}>{
namespaced?:boolean;
state:S;
getters?:Getters<S,R>;
mutations?:Mutations<S>;
actions?:Actions<S,R>;
modules?:ModuleTree<R>;
}
type GetterReturnType<G>={
[K in keyof G]:G[K] extends(...args:any)=>infer R?R:G[K];
};
type ActionContext<S,R>={
dispatch:Dispatch,
commit:MutationFn,
state:S,
rootState:R,
getters:Getters<S,R>
};
type StoreOptions<S>={
state:S,
getters?:Getters<S,S>,
mutations?:Mutations<S>,
actions?:Actions<S,S>,
modules?:ModuleTree<S>
};
//最终Store类型推导
class VuexStore<
S,
G extends Getters<S,S>,
M extends Mutations<S>,
A extends Actions<S,S>
>{
constructor(options:{
state:S & ThisType<Readonly<S>>,
getters?:G & ThisType<Readonly<GetterReturnType<G>>>,
mutations?:M & ThisType<void>,
actions?:A & ThisType<void>
}){}
get state():S{return {} as S;}
dispatch:K extends keyof A?A[K]:(...args)=>Promise<any>;
commit:K extends keyof M?Parameters<M[K]>[extends undefined?()=>void:P]> :never;
//动态模块注册
registerModule<N extends string,M>(path:N[],module:M):void{}
}
使用效果:
const store=new VuexStore({
state:{
count:0
},
mutations:{
increment(state){
state.count++;
}
},
actions:{
asyncIncrement({commit}){
setTimeout(()=>commit('increment'),100);
}
}});
store.commit('increment');//✅正确
store.commit('unknown');//❌错误提示
store.dispatch('asyncIncrement');//✅正确返回Promise
store.state.count.toFixed();//✅自动推导为number类型
关键点说明: -ThisType控制上下文类型推导路径模板字符串类型与条件类型的深度结合。 -递归类型处理嵌套modules的场景。 -infer关键字提取函数返回值类型。
##框架设计原理
###8.Vue3编译器优化细节解析
编译过程关键优化点:
输入模板:<div><span>{{msg}}</span></div>
传统Vue2编译结果:
with(this){return _c('div',[_c('span',[_v(_s(msg))])])}
存在的问题:-with语句导致作用域不可静态分析 -全量diff无法跳过静态节点
Vue3优化后输出:
import { createVNode as _createVNode } from "vue"
export function render(_ctx){
return (_openBlock(),
_createBlock("div",null,[_createVNode("span",null,_toDisplayString(_ctx.message))]))
}
核心优化技术:
####PatchFlag标记静态分析结果:
在生成的虚拟DOM节点中添加shapeFlag和patchFlag:
_createVNode("span",null,_toDisplayString(_ctx.message),/*TEXT*/8)
其中8表示只有文本内容会变化。
运行时根据这些标记可以跳过不必要的比较:
if(vnode.patchFlag & PatchFlags.TEXT){
hostSetElementText(el,vnode.children);
}else if(!optimized){
patchChildren(n,c,...);//全量diff
}
####Block Tree优化:
通过_openBlock()收集动态子节点形成一个block。在父级变动时可以直接跳过整个静态子树。
####静态提升(HoistStatic):
将纯静态节点提升到渲染函数外部:
原始模板中有多个<footer>Copyright</footer>会被编译为:
const _hoisted=_createVNode("footer",null,"Copyright");
function render(){
return [_hoisted,...];
}
##工程化体系
###9.Monorepo架构下的前端模块化设计
基于pnpm workspace的现代前端架构方案:
项目结构示例:
monorepo/
├── packages/
│ ├── shared/ #公共库
│ │ └── package.json
│ ├── react-components/ #React组件库
│ │ └── package.json
│ └── vue-components/ #Vue组件库
│ └── package.json
├── apps/
│ ├── admin-web/ #管理系统
│ │ └── package.json
│ └── mobile-h5/ #移动端H5
│ └── package.json
└── package.json
核心配置要点:
根package.json配置workspaces:
{
"workspaces":[
"packages/*",
"apps/*"
],
"scripts":{
"build":"pnpm -r run build"
}
}
模块依赖管理策略:
packages/react-components/package.json示例:
{
"name":"@mono/react-components",
"dependencies":{
"@mono/shared":"workspace:*",
"react":"^18"
},
peerDependencies:{
"react-dom":"^18"
}
}
构建工具链集成建议:
esbuild+Turborepo构建加速方案配置(turbo.json):
{ "pipeline":{
"build":{
"dependsOn":["^build"],
"outputs":[".dist/**"]
},
"test":{
"dependsOn":["build"],
"inputs":["src/**/*"]
}
} }
关键优势分析图表:
传统Multirepo vs Monorepo对比指标对比:
指标项 Multirepo Monorepo(with pnpm)
安装依赖时间 ⏱️25min 🚀90s
跨包重构难度 🔥高 👍低
CI缓存命中率 ❌30% ✅85%
磁盘空间占用 💾15GB 🪶4GB