前言
前端开发领域日新月异,技术栈不断演进。本文旨在为准备中级前端开发岗位面试的候选人提供一份全面的知识总结,涵盖主流前端框架、JavaScript/TypeScript核心概念以及浏览器相关的重要知识点。无论您是正在准备面试,还是希望系统梳理前端知识体系,这篇文章都将为您提供有价值的参考。
一、前端框架篇
1. React核心概念
Q1: React虚拟DOM的工作原理是什么?
虚拟DOM是React的核心概念之一,它是一个轻量级的JavaScript对象,是真实DOM的抽象表示。工作流程包括:
- 当组件状态变化时,React会创建新的虚拟DOM树
- 使用Diff算法比较新旧虚拟DOM树的差异
- 计算出最小变更集,批量更新真实DOM
优势:减少直接操作DOM的性能开销,实现高效的界面更新。
Q2: React Hooks解决了哪些问题?常用Hooks有哪些?
Hooks解决了类组件存在的几个问题:
- 状态逻辑难以复用(render props/HOC带来的嵌套问题)
- 复杂组件难以理解(生命周期中分散的逻辑)
- 类组件学习成本高(this指向问题)
常用Hooks:
useState: 管理组件状态useEffect: 处理副作用(替代生命周期)useContext: 访问ContextuseReducer: 复杂状态管理useCallback/useMemo: 性能优化useRef: 访问DOM或保存可变值
2. Vue核心概念
Q3: Vue2和Vue3的主要区别有哪些?
-
响应式系统:
- Vue2使用
Object.defineProperty - Vue3使用
Proxy,支持更多数据类型和更好的性能
- Vue2使用
-
组合式API:
- Vue3引入
setup函数和组合式API,逻辑组织更灵活
- Vue3引入
-
性能优化:
- Vue3的虚拟DOM重写,支持静态提升等优化
- 更小的包体积(Tree-shaking支持)
-
TypeScript支持:
- Vue3提供更好的TS类型支持
-
生命周期变化:
beforeCreate和created被setup替代- 其他生命周期添加
on前缀(如onMounted)
Q4: Vue的响应式原理如何实现?
Vue2实现:
- 通过
Object.defineProperty劫持数据属性的getter/setter - 每个组件实例对应一个Watcher实例
- 依赖收集:getter中收集依赖(Watcher)
- 派发更新:setter中通知依赖更新
Vue3实现:
- 使用
Proxy代理整个对象 - 通过
track和trigger函数实现依赖收集和派发更新 - 支持更多数据类型(如Map、Set等)
3. 框架对比与选型
Q5: React和Vue的主要区别是什么?
| 方面 | React | Vue |
|---|---|---|
| 设计理念 | 函数式编程,单向数据流 | 渐进式框架,双向绑定 |
| 模板语法 | JSX | 基于HTML的模板语法 |
| 状态管理 | 需要配合Redux/MobX等 | 内置Vuex/Pinia |
| 学习曲线 | 较高(需理解函数式概念) | 较低(更接近传统Web开发) |
| 灵活性 | 更高(可搭配多种库) | 更约定俗成 |
| 性能 | 虚拟DOM优化 | 虚拟DOM+响应式系统优化 |
| 社区生态 | 更庞大 | 增长迅速 |
二、JavaScript核心篇
1. 作用域与闭包
Q6: 解释JavaScript中的作用域链和闭包
作用域链:
- 函数执行时会创建执行上下文,包含变量对象、作用域链和this
- 作用域链是变量对象的链表,用于标识符解析
- 内部函数可以访问外部函数的作用域
闭包:
- 函数可以记住并访问所在的词法作用域,即使函数在当前作用域外执行
- 常见应用:模块模式、私有变量、函数工厂、高阶函数等
function createCounter() {
let count = 0;
return function() {
return ++count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
2. 异步编程
Q7: 解释Event Loop和异步执行机制
JavaScript是单线程语言,通过Event Loop实现异步:
-
调用栈:同步代码按顺序执行
-
任务队列:
- 宏任务队列:setTimeout、setInterval、I/O等
- 微任务队列:Promise.then、MutationObserver等
-
执行顺序:
- 执行同步代码(调用栈)
- 执行当前所有微任务
- 执行一个宏任务
- 重复上述过程
console.log('1');
setTimeout(() => console.log('2'), 0);
Promise.resolve().then(() => console.log('3'));
console.log('4');
// 输出顺序:1, 4, 3, 2
3. 原型与继承
Q8: 解释JavaScript的原型链继承
- 每个对象都有
__proto__属性指向其构造函数的prototype - 访问对象属性时,会沿着原型链向上查找
- 原型链的终点是
Object.prototype.__proto__(null)
function Person(name) {
this.name = name;
}
Person.prototype.sayName = function() {
console.log(this.name);
};
function Student(name, grade) {
Person.call(this, name);
this.grade = grade;
}
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;
const s = new Student('Tom', 5);
s.sayName(); // Tom
ES6引入class语法糖,底层仍是基于原型的继承。
4. ES6+新特性
Q9: 列举并解释常用的ES6+特性
- let/const:块级作用域变量声明
- 箭头函数:简洁语法,不绑定this
- 模板字符串:支持多行和插值
- 解构赋值:从数组/对象中提取值
- 默认参数:函数参数默认值
- 展开/剩余运算符:
...操作符 - Promise:更好的异步处理
- 模块化:
import/export - 类语法:
class关键字 - Symbol:唯一值
- Map/Set:新的数据结构
- Proxy/Reflect:元编程
- 可选链(?.):安全访问嵌套属性
- 空值合并(??):提供默认值
- async/await:同步风格写异步代码
三、TypeScript篇
1. 基础概念
Q10: TypeScript相比JavaScript有哪些优势?
- 静态类型检查:编译时发现类型错误
- 更好的代码提示:IDE支持更完善
- 代码可维护性:大型项目更易维护
- 渐进式采用:可逐步迁移
- 现代JavaScript支持:支持最新ECMAScript特性
- 丰富的类型系统:接口、泛型、联合类型等
- 更好的重构能力:类型信息支持安全重构
2. 核心特性
Q11: 解释TypeScript中的接口和类型别名的区别
| 特性 | 接口(interface) | 类型别名(type) |
|---|---|---|
| 扩展方式 | 使用extends继承 | 使用&交叉类型 |
| 合并声明 | 支持同名接口自动合并 | 不支持 |
| 实现类 | 可以被类实现(implements) | 不能直接被类实现 |
| 原始类型 | 只能描述对象类型 | 可以描述任意类型 |
| 性能 | 更适合声明形状 | 复杂类型计算可能影响性能 |
// 接口
interface Person {
name: string;
}
interface Employee extends Person {
salary: number;
}
// 类型别名
type Person = {
name: string;
};
type Employee = Person & { salary: number };
3. 高级类型
Q12: 解释TypeScript中的泛型及其应用场景
泛型允许创建可重用的组件,这些组件可以支持多种类型:
function identity<T>(arg: T): T {
return arg;
}
// 使用
const output = identity<string>("hello");
const output2 = identity("hello"); // 类型推断
应用场景:
- 函数/方法:创建类型安全的工具函数
- 类:创建可重用的数据结构(如集合)
- 接口:定义灵活的形状
- 约束类型:使用
extends约束泛型范围
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
四、浏览器相关篇
1. 渲染原理
Q13: 描述浏览器渲染页面的过程
- 解析HTML:构建DOM树
- 解析CSS:构建CSSOM树
- 合并DOM和CSSOM:形成渲染树(Render Tree)
- 布局(Layout):计算元素的位置和大小
- 绘制(Paint):将渲染树转换为屏幕上的像素
- 合成(Composite):将各层合并显示
关键点:
- 重排(Reflow):布局改变(影响性能)
- 重绘(Repaint):外观改变不影响布局
- 优化:减少DOM操作,使用transform/opacity等属性(触发合成层)
2. 性能优化
Q14: 前端性能优化有哪些常用手段?
-
资源优化:
- 压缩代码和资源
- 使用CDN
- 图片优化(WebP、懒加载)
- 代码分割和按需加载
-
渲染优化:
- 减少重排重绘
- 使用requestAnimationFrame
- 虚拟列表优化长列表
- 避免布局抖动
-
缓存策略:
- 合理设置HTTP缓存头
- Service Worker缓存
- 数据缓存(内存、localStorage)
-
网络优化:
- HTTP/2
- 预加载/prefetch
- 减少请求数量(雪碧图、字体图标)
-
代码优化:
- 防抖节流
- Web Worker处理耗时任务
- 避免内存泄漏
3. 安全相关
Q15: 常见的前端安全问题和防御措施
-
XSS(跨站脚本攻击):
- 防御:输入输出编码,CSP策略,HttpOnly Cookie
-
CSRF(跨站请求伪造):
- 防御:SameSite Cookie,CSRF Token,验证Referer
-
点击劫持:
- 防御:X-Frame-Options,Frame Busting代码
-
中间人攻击:
- 防御:HTTPS,HSTS
-
信息泄露:
- 防御:避免敏感信息在前端处理,设置安全头
五、综合实战题
Q16: 如何设计一个前端权限控制系统?
-
路由权限:
- 定义路由元信息(meta)包含权限要求
- 路由守卫检查用户权限
- 动态生成菜单和路由表
-
组件权限:
- 创建权限指令(v-permission)
- 高阶组件包裹需要权限控制的组件
-
API权限:
- 请求拦截器检查权限
- 后端同时验证
-
数据权限:
- 根据权限过滤返回数据
- 前端处理UI展示逻辑
-
存储方案:
- JWT存储权限信息
- 合理设置token过期时间
示例代码:
// 路由守卫
router.beforeEach((to, from, next) => {
const requiredRoles = to.meta.roles;
if (!requiredRoles) return next();
const userRoles = store.getters.roles;
if (hasPermission(requiredRoles, userRoles)) {
next();
} else {
next('/403');
}
});
// 权限指令
Vue.directive('permission', {
inserted(el, binding, vnode) {
const { value } = binding;
const roles = store.getters.roles;
if (value && !hasPermission(value, roles)) {
el.parentNode && el.parentNode.removeChild(el);
}
}
});
六、面试技巧与准备建议
- 理解原理而非死记硬背:面试官更关注你对概念的理解深度
- 准备项目案例:能够详细解释你做过的项目和技术选型
- 练习编码题:LeetCode、Codewars等平台保持手感
- 了解公司技术栈:针对性地准备相关框架知识
- 准备问题提问:展示你对职位和技术的兴趣
- 模拟面试:找朋友或使用在线平台进行模拟练习