前端面试知识梳理大全
🎯 目标:帮助不同层级的前端工程师系统化准备面试,涵盖一线互联网公司真实面试题
📊 适用范围:阿里巴巴、腾讯、字节跳动、美团、京东、百度等一线互联网公司
🔄 更新频率:持续更新最新技术趋势和面试题目
📚 目录索引
🎯 按工作经验分层
📋 按题型分类
🔧 按技术栈分类
初级工程师(1-2年经验)
基础概念题
1. JavaScript数据类型和类型检测
问题描述:
请详细说明JavaScript的数据类型,并解释如何准确检测各种数据类型?
核心考点:
- 基本数据类型和引用数据类型的理解
- 类型检测方法的掌握程度
- 对JavaScript类型系统的认知
标准答案:
JavaScript数据类型分为两大类:
基本数据类型(7种):
undefined
- 未定义null
- 空值boolean
- 布尔值number
- 数字string
- 字符串symbol
- 符号(ES6新增)bigint
- 大整数(ES2020新增)
引用数据类型(1种):
object
- 对象(包括普通对象、数组、函数、日期等)
类型检测方法:
- typeof操作符:
typeof undefined // "undefined"
typeof null // "object" (历史遗留问题)
typeof true // "boolean"
typeof 42 // "number"
typeof "hello" // "string"
typeof Symbol() // "symbol"
typeof 123n // "bigint"
typeof {
} // "object"
typeof [] // "object"
typeof function(){
} // "function"
- Object.prototype.toString.call():
Object.prototype.toString.call(null) // "[object Null]"
Object.prototype.toString.call([]) // "[object Array]"
Object.prototype.toString.call(new Date()) // "[object Date]"
Object.prototype.toString.call(/regex/) // "[object RegExp]"
- instanceof操作符:
[] instanceof Array // true
new Date() instanceof Date // true
- Array.isArray():
Array.isArray([]) // true
Array.isArray({
}) // false
代码示例:
// 通用类型检测函数
function getType(value) {
if (value === null) return 'null';
if (typeof value !== 'object') return typeof value;
const objectType = Object.prototype.toString.call(value);
return objectType.slice(8, -1).toLowerCase();
}
// 测试
console.log(getType(null)); // "null"
console.log(getType([])); // "array"
console.log(getType(new Date())); // "date"
console.log(getType(/regex/)); // "regexp"
扩展知识点:
typeof null
返回 “object” 的历史原因- Symbol类型的使用场景和特性
- BigInt类型解决的精度问题
- 类型转换的隐式规则
难度标记:⭐
频率标记:🔥🔥🔥
2. 闭包的概念和应用
问题描述:
什么是闭包?请举例说明闭包的实际应用场景。
核心考点:
- 闭包的定义和形成条件
- 作用域链的理解
- 闭包的实际应用能力
标准答案:
闭包定义:
闭包是指有权访问另一个函数作用域中变量的函数。简单说,闭包就是函数内部的函数可以访问外部函数的变量。
形成条件:
- 函数嵌套
- 内部函数引用外部函数的变量
- 内部函数被外部调用或返回
代码示例:
// 基础闭包示例
function outerFunction(x) {
// 外部函数的变量
let outerVariable = x;
// 内部函数(闭包)
function innerFunction(y) {
console.log(outerVariable + y); // 访问外部变量
}
return innerFunction;
}
const closure = outerFunction(10);
closure(5); // 输出: 15
实际应用场景:
- 模块化封装:
const Calculator = (function() {
let result = 0; // 私有变量
return {
add: function(num) {
result += num;
return this;
},
subtract: function(num) {
result -= num;
return this;
},
getResult: function() {
return result;
}
};
})();
Calculator.add(10).subtract(3).getResult(); // 7
- 函数柯里化:
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn.apply(this, args);
} else {
return function(...nextArgs) {
return curried.apply(this, args.concat(nextArgs));
};
}
};
}
const add = (a, b, c) => a + b + c;
const curriedAdd = curry(add);
console.log(curriedAdd(1)(2)(3)); // 6
- 防抖和节流:
function debounce(func, delay) {
let timeoutId;
return function(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => func.apply(this, args), delay);
};
}
const debouncedSearch = debounce(function(query) {
console.log('搜索:', query);
}, 300);
扩展知识点:
- 闭包的内存泄漏问题
- 垃圾回收机制对闭包的影响
- 箭头函数中的闭包特性
- 闭包在异步编程中的应用
难度标记:⭐⭐
频率标记:🔥🔥🔥
3. 原型和原型链
问题描述:
请解释JavaScript中的原型和原型链机制,以及它们在继承中的作用。
核心考点:
- 原型对象的概念
- 原型链的查找机制
- 继承的实现原理
标准答案:
原型(Prototype):
每个JavaScript对象都有一个原型对象,原型对象也是一个普通对象。对象可以从原型对象继承属性和方法。
原型链(Prototype Chain):
当访问对象的属性时,如果对象本身没有该属性,JavaScript会沿着原型链向上查找,直到找到该属性或到达原型链的顶端(null)。
关键概念:
__proto__
:对象的原型引用(非标准,但广泛支持)prototype
:函数的原型属性constructor
:指向构造函数的引用
代码示例:
// 构造函数
function Person(name) {
this.name = name;
}
// 在原型上添加方法
Person.prototype.sayHello = function() {
console.log(`Hello, I'm ${
this.name}`);
};
// 创建实例
const person1 = new Person('Alice');
const person2 = new Person('Bob');
// 原型链查找
person1.sayHello(); // "Hello, I'm Alice"
// 验证原型关系
console.log(person1.__proto__ === Person.prototype); // true
console.log(Person.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__ === null); // true
继承实现:
// 父类
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function() {
console.log(`${
this.name} makes a sound`);
};
// 子类
function Dog(name, breed) {
Animal.call(this, name); // 调用父类构造函数
this.breed = breed;
}
// 设置原型链继承
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
// 子类特有方法
Dog.prototype.bark = function() {
console.log(`${
this.name} barks`);
};
const dog = new Dog('Buddy', 'Golden Retriever');
dog.speak(); // "Buddy makes a sound"
dog.bark(); // "Buddy barks"
ES6 Class语法:
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${
this.name} makes a sound`);
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name);
this.breed = breed;
}
bark() {
console.log(`${
this.name} barks`);
}
}
扩展知识点:
Object.create()
的使用和原理instanceof
操作符的工作机制- 原型污染的安全问题
- Mixin模式的实现
难度标记:⭐⭐
频率标记:🔥🔥🔥
原理深入题
4. 事件循环机制
问题描述:
请详细解释JavaScript的事件循环机制,包括宏任务和微任务的执行顺序。
核心考点:
- 单线程执行模型的理解
- 宏任务和微任务的区别
- 异步编程的底层机制
标准答案:
事件循环(Event Loop):
JavaScript是单线程语言,事件循环是JavaScript处理异步操作的机制,它决定了代码的执行顺序。
执行栈(Call Stack):
同步代码按顺序执行,形成执行栈。
任务队列(Task Queue):
- 宏任务(Macro Task):setTimeout、setInterval、I/O操作、UI渲染
- 微任务(Micro Task):Promise.then、queueMicrotask、MutationObserver
执行顺序:
- 执行同步代码
- 执行所有微任务
- 执行一个宏任务
- 重复步骤2-3
代码示例:
console.log('1'); // 同步任务
setTimeout(() => {
console.log('2'); // 宏任务
}, 0);
Promise.resolve().then(() => {
console.log('3'); // 微任务
});
console.log('4'); // 同步任务
// 输出顺序: 1, 4, 3, 2
复杂示例:
console.log('start');
setTimeout(() => {
console.log('timeout1');
Promise.resolve().then(() => {
console.log('promise1');
});
}, 0);
Promise.resolve().then(() => {
console.log('promise2');
setTimeout(() => {
console.log('timeout2');
}, 0);
});
console.log('end');
// 输出顺序: start, end, promise2, timeout1, promise1, timeout2
扩展知识点:
- Node.js中的事件循环差异
- async/await的执行机制
- requestAnimationFrame的执行时机
- 浏览器渲染与事件循环的关系
难度标记:⭐⭐⭐
频率标记:🔥🔥🔥
中级工程师(3-5年经验)
实战场景题
5. 性能优化实战
问题描述:
在一个大型单页应用中,你发现首屏加载时间过长,请描述你的性能优化思路和具体实施方案。
核心考点:
- 性能分析能力
- 优化方案的系统性思考
- 实际项目经验
标准答案:
性能分析步骤:
- 性能指标测量:
// 使用Performance API测量关键指标
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.entryType === 'navigation') {
console.log('DNS查询时间:', entry.domainLookupEnd - entry.domainLookupStart);
console.log('TCP连接时间:', entry.connectEnd - entry.connectStart);
console.log('首字节时间:', entry.responseStart - entry.requestStart);
console.log('DOM解析时间:', entry.domContentLoadedEventEnd - entry.responseEnd);
}
}
});
observer.observe({
entryTypes: ['navigation'] });
- 资源加载分析:
// 分析资源加载性能
const resources = performance.getEntriesByType('resource');
const largeResources = resources.filter(resource =>
resource.transferSize > 100 * 1024 // 大于100KB的资源
);
console.log('大文件资源:', largeResources);
优化方案:
- 代码分割和懒加载:
// 路由级别的代码分割
const routes = [
{
path: '/home',
component: () => import(/* webpackChunkName: "home" */ '@/views/Home.vue')
},
{
path: '/dashboard',
component: () => import(/* webpackChunkName: "dashboard" */ '@/views/Dashboard.vue')
}
];
// 组件级别的懒加载
const LazyComponent = defineAsyncComponent({
loader: () => import('./HeavyComponent.vue'),
loadingComponent: LoadingSpinner,
errorComponent: ErrorComponent,
delay: 200,
timeout: 3000
});
- 资源优化:
// 图片懒加载
const useImageLazyLoad = () => {
const imageObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.classList.remove('lazy');
imageObserver.unobserve(img);
}
});
});
const observeImages = () => {
const lazyImages = document.querySelectorAll('img[data-src]');
lazyImages.forEach(img => imageObserver.observe(img));
};
return {
observeImages };
};
- 缓存策略:
// Service Worker缓存
self.addEventListener('fetch', event => {
if (event.request.destination === 'image') {
event.respondWith(
caches.open('images-cache').then(cache => {
return cache.match(event.request).then(response => {
if (response) {
return response;
}
return fetch(event.request).then(fetchResponse => {
cache.put(event.request, fetchResponse.clone());
return fetchResponse;
});
});
})
);
}
});
- 预加载策略:
// 关键资源预加载
const preloadCriticalResources = () => {
const criticalResources = [
'/api/user/profile',
'/api/dashboard/summary'
];
criticalResources.forEach(url => {
fetch(url, {
method: 'GET',
priority: 'high'
}).then(response => {
// 缓存响应数据
caches.open('api-cache').then(cache => {
cache.put(url, response.clone());
});
});
});
};
量化结果:
- 首屏加载时间从3.2s优化到1.8s(减少44%)
- 资源体积从2.1MB减少到1.3MB(减少38%)
- Lighthouse性能评分从65分提升到92分
扩展知识点:
- Core Web Vitals指标优化
- HTTP/2和HTTP/3的性能优势
- CDN配置和边缘计算
- 构建工具的优化配置
难度标记:⭐⭐⭐
频率标记:🔥🔥🔥
高级工程师(5年以上经验)
架构设计题
6. 微前端架构设计
问题描述:
设计一个支持多团队协作的微前端架构,需要考虑技术选型、通信机制、部署策略等方面。
核心考点:
- 大型系统架构设计能力
- 技术选型的权衡考虑
- 团队协作的技术方案
标准答案:
架构设计思路:
- 技术选型对比:
方案 | 优势 | 劣势 | 适用场景 |
---|---|---|---|
Single-SPA | 成熟稳定、生态丰富 | 学习成本高 | 大型企业应用 |
qiankun | 开箱即用、阿里维护 | 定制化程度低 | 中大型项目 |
Module Federation | Webpack原生支持 | 版本依赖复杂 | 现代化项目 |
iframe | 隔离性好、简单 | 性能差、体验差 | 遗留系统集成 |
- 架构设计方案:
// 主应用架构
interface MicroFrontendConfig {
name: string;
entry: string;
container: string;
activeRule: string;
props?: Record<string, any>;
}
class MicroFrontendManager {
private apps: Map<string, MicroFrontendConfig> = new Map();
private globalState: GlobalStateManager;
private eventBus: EventBus;
constructor() {
this.globalState = new GlobalStateManager();
this.eventBus = new EventBus();
this.setupGlobalErrorHandler();
}
// 注册微应用
registerApp(config: MicroFrontendConfig) {
this.apps.set(config.name, config);
// 配置应用间通信
this.setupAppCommunication(config.name);
}
// 应用间通信机制
private setupAppCommunication(appName: string) {
// 1. 全局状态共享
this.globalState.subscribe(appName, (state) => {
this.eventBus.emit(`${
appName}:state-change`, state);
});
// 2. 事件总线
this.eventBus.on(`${
appName}:action`, (payload) => {
this.handleAppAction(appName, payload);
});
}
// 全局错误处理
private setupGlobalErrorHandler() {
window.addEventListener('error', (event) => {
this.reportError({
type: 'javascript-error',
message: event.message,
filename: event.filename,
lineno: event.lineno,
colno: event.colno,
stack: event.error?.stack
});
});
window.addEventListener('unhandledrejection', (event) => {
this.reportError({
type: 'promise-rejection',
reason: event.reason
});
});
}
}
- 通信机制设计:
// 全局状态管理
class GlobalStateManager {
private state: Map<string, any> = new Map();
private subscribers: Map<string, Set<Function>> = new Map();
setState(key: string, value: any) {
const oldValue = this.state.get(key);
this.state.set(key, value);
// 通知订阅者
this.notifySubscribers(key, value, oldValue);
}
getState(key: string) {
return this.state.get(key);
}
subscribe(key: string, callback: Function) {
if (!this.subscribers.has(key)) {
this.subscribers.set(key, new Set());
}
this.subscribers.get(key)!.add(callback);
// 返回取消订阅函数
return () => {
this.subscribers.get(key)?.delete(callback);
};
}
private notifySubscribers(key: string, newValue: any, oldValue: any) {
const callbacks = this.subscribers.get(key);
if (callbacks) {
callbacks.forEach(callback => {
try {
callback(newValue, oldValue);
} catch (error) {
console.error('State subscriber error:', error);
}
});
}
}
}
// 事件总线
class EventBus {
private events: Map<string, Set<Function>> = new Map();
on(event: string, callback: Function) {
if (!this.events.has(event)) {
this.events.set(event, new Set());
}
this.events.get(event)!.add(callback);
}
emit(event: string, ...args: any[]) {
const callbacks = this.events.get(event);
if (callbacks) {
callbacks.forEach(callback => {
try {
callback(...args);
} catch (error) {
console.error('Event callback error:', error);
}
});
}
}
off(event: string, callback: Function) {
this.events.get(event)?.delete(callback);
}
}
- 部署策略:
# Docker部署配置
version: '3.8'
services:
# 主应用
shell-app:
build: ./apps/shell
ports:
- "3000:80"
environment:
- NODE_ENV=production
depends_on:
- user-app
- order-app
- product-app
# 用户中心微应用
user-app:
build: ./apps/user
ports:
- "3001:80"
environment:
- NODE_ENV=production
# 订单系统微应用
order-app:
build: ./apps/order
ports:
- "3002:80"
environment:
- NODE_ENV=production
# 商品管理微应用
product-app:
build: ./apps/product
ports:
- "3003:80"
environment:
- NODE_ENV=production
# Nginx网关
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- shell-app
- 监控和治理:
// 微前端监控系统
class MicroFrontendMonitor {
private performanceMetrics: Map<string, any> = new Map();
private errorReports: Array<any> = [];
// 性能监控
trackPerformance(appName: string) {
const observer = new PerformanceObserver((list) => {
const entries = list.getEntries();
entries.forEach(entry => {
if (entry.entryType === 'navigation') {
this.performanceMetrics.set(appName, {
loadTime: entry.loadEventEnd - entry.loadEventStart,
domContentLoaded: entry.domContentLoadedEventEnd - entry.domContentLoadedEventStart,
firstPaint: performance.getEntriesByName('first-paint')[0]?.startTime,
firstContentfulPaint: performance.getEntriesByName('first-contentful-paint')[0]?.startTime
});
}
});
});
observer.observe({
entryTypes: ['navigation'] });
}
// 错误监控
trackErrors(appName: string) {
window.addEventListener('error', (event) => {
this.errorReports.push({
app: appName,
type: 'javascript-error',
message: event.message,
filename: event.filename,
timestamp: Date.now()
});
});
}
// 生成监控报告
generateReport() {
return {
performance: Object.fromEntries(this.performanceMetrics),
errors: this.errorReports,
timestamp: Date.now()
};
}
}
扩展知识点:
- 微前端的样式隔离方案
- 共享依赖的版本管理
- 渐进式迁移策略
- 性能优化和监控体系
难度标记:⭐⭐⭐
频率标记:🔥🔥
📊 面试准备策略
🎯 不同层级的重点准备方向
初级工程师:
- 重点掌握JavaScript基础概念
- 熟练使用Vue/React基本API
- 了解基础的工程化工具
- 能够解决常见的开发问题
中级工程师:
- 深入理解框架原理和设计思想
- 具备性能优化的实战经验
- 掌握复杂业务场景的解决方案
- 能够进行技术选型和架构设计
高级工程师:
- 具备大型系统的架构设计能力
- 拥有团队技术管理经验
- 能够解决复杂的技术难题
- 具备前瞻性的技术视野
📚 推荐学习资源
官方文档:
进阶学习:
- 《JavaScript高级程序设计》
- 《你不知道的JavaScript》
- 《深入理解ES6》
- 《前端架构:从入门到微前端》
实战项目:
- 参与开源项目贡献
- 搭建个人技术博客
- 实现经典算法和数据结构
- 构建完整的全栈项目
JavaScript核心
7. 深拷贝和浅拷贝的实现
问题描述:
请实现一个深拷贝函数,并说明与浅拷贝的区别。需要考虑各种数据类型和边界情况。
核心考点:
- 对象引用和值传递的理解
- 递归算法的应用
- 边界情况的处理能力
标准答案:
浅拷贝 vs 深拷贝:
- 浅拷贝:只复制对象的第一层属性,嵌套对象仍然是引用
- 深拷贝:递归复制对象的所有层级,创建完全独立的副本
浅拷贝实现:
// 方法1:Object.assign
const shallowCopy1 = Object.assign({
}, originalObj);
// 方法2:扩展运算符
const shallowCopy2 = {