前端面试常考基础题目详解
一、HTML/CSS 高频考点
1. 盒模型
• 标准盒模型(box-sizing: content-box):元素的宽度/高度仅包含内容区域,内边距(padding)和边框(border)会增加整体尺寸。
• IE盒模型(box-sizing: border-box):元素的宽度/高度包含内容、内边距和边框,适合布局计算。
• 应用场景:通过调整盒模型可避免布局错位,例如响应式设计常用border-box简化计算。
2. BFC(块级格式化上下文)
• 触发条件:float非none、overflow非visible、position: absolute/fixed等。
• 作用:隔离渲染区域,解决浮动元素导致的父元素高度塌陷、外边距重叠等问题。
• 示例:清除浮动时,父元素设置overflow: hidden即可触发BFC。
3. Flex布局与Grid布局
• Flex:适合一维布局,通过justify-content和align-items实现快速居中。
• Grid:适合二维布局,place-items: center可一键实现水平垂直居中。
• 对比:Flex更灵活,Grid更适合复杂网格结构(如仪表盘)。
4. 响应式设计实现
• 媒体查询:@media (max-width: 768px)适配不同屏幕尺寸。
• 视口单位:使用vw、vh实现基于视口的动态尺寸。
• 示例:移动端1像素边框可通过transform: scaleY(0.5)缩放实现。
5. CSS选择器优先级
• 优先级顺序:!important > 内联样式 > ID选择器 > 类/伪类 > 元素选择器 > 通配符。
• 计算规则:权重由(行内, ID数, 类数, 元素数)组成,如.class a的权重为(0,0,1,1)。
二、JavaScript 核心问题
1. 闭包(Closure)
• 定义:函数能访问并记住其词法作用域,即使函数在外部执行。
• 用途:实现私有变量、防抖/节流函数、模块化开发。
• 风险:不当使用可能导致内存泄漏(如未释放的DOM引用)。
2. 原型链与继承
• 原型链:通过__proto__实现对象间的继承关系,prototype是构造函数的属性。
• ES6继承:class语法糖基于原型链,对比ES5的寄生组合继承更简洁。
3. 事件循环(Event Loop)
• 机制:同步任务进入主线程,异步任务(如setTimeout、Promise)由任务队列管理。
• 执行顺序:宏任务(脚本、I/O) → 微任务(Promise.then) → UI渲染。
4. 防抖(Debounce)与节流(Throttle)
• 防抖:连续触发时只执行最后一次(如搜索框输入)。
function debounce(fn, delay) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), delay);
};
}
• 节流:固定时间间隔执行一次(如滚动事件)。
5. ES6+特性
• 模块化:import/export替代require/module.exports,支持静态加载。
• 箭头函数:无自身this,继承外层作用域的this,简化回调。
三、框架与工具(React/Vue)
1. React中key的作用
• 虚拟DOM优化:key帮助React识别元素变化,减少不必要的重渲染。
• 示例:列表删除中间项时,无key会导致后续元素全部重新渲染。
2. Vue双向绑定原理
• 数据劫持:通过Object.defineProperty或Proxy监听数据变化。
• 发布-订阅:数据变更时触发视图更新,视图输入触发数据修改(如v-model)。
3. Redux与Vuex设计思想
• 共同点:单一数据源、状态不可变。
• 差异:Redux依赖纯函数Reducer,Vuex集成Vue响应式系统。
四、性能优化与浏览器
1. 减少重绘与回流
• 回流(Reflow):布局变化(如修改宽度)引发渲染树重新计算。
• 优化策略:使用transform代替top/left,避免频繁操作DOM。
2. HTTP缓存策略
• 强缓存:Cache-Control: max-age=3600直接复用本地资源。
• 协商缓存:ETag和Last-Modified验证资源是否更新。
3. Webpack优化
• 代码分割:SplitChunksPlugin拆分公共代码,按需加载。
• Tree Shaking:删除未引用代码(需ES6模块语法)。
五、高频手写代码题
1. 深拷贝函数
function deepClone(obj, map = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj;
if (map.has(obj)) return map.get(obj);
const clone = Array.isArray(obj) ? [] : {};
map.set(obj, clone);
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
clone[key] = deepClone(obj[key], map);
}
}
return clone;
}
2. 数组去重
const unique = arr => [...new Set(arr)];
// 或
const unique = arr => arr.filter((item, index) => arr.indexOf(item) === index);
总结与建议
以上题目覆盖了前端面试中80%以上的高频考点,建议结合实际项目经验进行深度理解,例如用BFC解决布局问题、用闭包优化性能等。更多完整题目可参考来源文档。
---
接下来,思考如何扩展现有部分:
1. HTML/CSS:
• 增加HTML5新特性,如语义化标签(header, nav, main, footer, article, section),表单验证属性(required, pattern),多媒体标签(video, audio)。参考网页[2][4]。
• 补充CSS动画(transition和animation的区别),响应式设计的媒体查询示例,视口单位的使用。参考网页[2][4][6]。
2. JavaScript:
• 添加ES6+的更多特性,如解构赋值、模板字符串、模块化导入导出。参考网页[1][2][6]。
• 扩展事件循环,详细讲解宏任务和微任务,给出执行顺序的例子。参考网页[2][4][6]。
• 增加Promise的链式调用、async/await的错误处理。参考网页[1][3][6]。
3. 框架与工具:
• 补充React Hooks的使用(useState, useEffect),Vue3的Composition API。参考网页[6][7]。
• 增加Webpack的常见配置,如Loader和Plugin的区别,代码分割的实现。参考网页[1][5][7]。
4. 浏览器与网络:
• 扩展HTTP缓存策略,强缓存和协商缓存的头部字段,如Cache-Control, ETag, Last-Modified。参考网页[2][5][6]。
• 补充跨域解决方案,如CORS、JSONP、PostMessage。参考网页[4][6]。
5. 手写代码题:
• 添加更多常见手写题,如Promise.all实现、数组扁平化、柯里化函数。参考网页[2][7][8]。
需要确保每个新增内容都有对应的引用来源,并正确标注。例如,HTML5新特性来自网页[2]和[4],CSS动画参考网页[2][6],ES6+特性来自网页[1][2][6]等。同时,注意不要重复已有的内容,而是补充新的知识点或更详细的解释。
最后,检查所有引用是否准确,信息是否相关,结构是否清晰,确保回答满足用户需求,提供更多有价值的前端面试基础题目详解。
前端面试常考基础题目扩展详解
一、HTML/CSS 扩展考点
1. HTML5 新特性
• 语义化标签:<header>, <nav>, <main>, <article>, <section> 等,提升代码可读性和 SEO 效果。
• 表单验证:新增属性如 required(必填)、pattern(正则校验)、type="email"(邮箱输入),简化客户端验证逻辑。
• 多媒体支持:<video> 和 <audio> 标签实现音视频嵌入,支持播放控制;<canvas> 用于绘制动态图形,<svg> 用于矢量图渲染。
2. CSS 动画与响应式
• Transition vs Animation:
◦ transition 用于简单属性过渡(如 hover 效果),需触发条件;
◦ animation 通过关键帧(@keyframes)实现复杂动画,可循环播放。
• 媒体查询优化:
@media (min-width: 768px) and (max-width: 1024px) {
/* 适配平板设备 */
}
结合 vw/vh 视口单位,实现动态布局调整。
二、JavaScript 扩展核心问题
1. ES6+ 特性深化
• 解构赋值:快速提取对象或数组数据,如:
const { name, age } = user;
const [first, ...rest] = [1, 2, 3];
• 模板字符串:支持多行文本和变量插值,如:
const greeting = `Hello, ${name}!`;
• 模块化:import/export 实现代码拆分,支持按需加载(如动态 import())。
2. 事件循环与异步进阶
• 宏任务与微任务执行顺序:
示例:
setTimeout(() => console.log('宏任务'), 0);
Promise.resolve().then(() => console.log('微任务'));
// 输出顺序:微任务 → 宏任务
微任务(Promise.then、MutationObserver)优先于宏任务(setTimeout、setInterval)执行。
• Async/Await 错误处理:
async function fetchData() {
try {
const res = await fetch(url);
} catch (error) {
console.error('请求失败:', error);
}
}
三、框架与工具扩展
1. React 进阶
• Hooks 核心:
◦ useState 管理组件状态,useEffect 处理副作用(数据请求、事件监听)。
◦ 自定义 Hooks 封装复用逻辑(如 useFetch 数据请求)。
• 虚拟 DOM 优化:通过 Diff 算法减少 DOM 操作,key 属性提升列表渲染效率。
2. Webpack 工程化配置
• Loader 与 Plugin 区别:
◦ Loader 处理文件转换(如 babel-loader 转译 ES6);
◦ Plugin 扩展功能(如 HtmlWebpackPlugin 生成 HTML 入口)。
• Tree Shaking:通过 ES6 模块语法(import/export)移除未使用代码,需配合 sideEffects 配置。
四、浏览器与网络扩展
1. HTTP 缓存策略
• 强缓存:
◦ Cache-Control: max-age=3600 表示资源缓存 1 小时;
◦ Expires 基于绝对时间,优先级低于 Cache-Control。
• 协商缓存:
◦ ETag(哈希值)和 Last-Modified(最后修改时间)由服务器验证资源是否更新。
2. 跨域解决方案
• CORS:通过响应头 Access-Control-Allow-Origin: * 允许跨域请求。
• JSONP:动态创建 <script> 标签绕过同源限制,仅支持 GET 请求。
• PostMessage:window.postMessage 实现跨窗口通信(如嵌入 iframe)。
五、高频手写代码扩展
1. Promise 相关
• 实现 Promise.all:
function promiseAll(promises) {
return new Promise((resolve, reject) => {
let results = [];
let completed = 0;
promises.forEach((promise, index) => {
promise.then(res => {
results[index] = res;
if (++completed === promises.length) resolve(results);
}).catch(reject);
});
});
}
• 数组扁平化:
const flatten = arr => arr.reduce((acc, cur) =>
acc.concat(Array.isArray(cur) ? flatten(cur) : cur), []
);
总结与建议
以上扩展内容覆盖了 HTML5 语义化、CSS 动画、ES6+ 高级语法、框架底层原理及工程化工具配置,建议结合项目实践深入理解,例如:
• 用 postMessage 实现微前端通信;
• 通过 Webpack 代码分割优化首屏加载速度。
---
更多 JavaScript 基础题目详解(附答案)
一、数据类型与类型转换
1. 数据类型
• 基本类型:number, string, boolean, null, undefined, symbol, bigint。
• 引用类型:object(包含 array, function 等子类型)。
• 特殊值:typeof null 返回 "object"(历史遗留问题)。
2. 类型比较
• == vs ===:== 会进行隐式类型转换(如 "5" == 5 为 true),=== 严格比较类型和值。
• 示例:0.1 + 0.2 !== 0.3,因 IEEE 754 浮点数精度丢失。
二、作用域与闭包
1. 作用域链与变量提升
• 变量提升:var 声明的变量会提升到作用域顶部,值为 undefined;let/const 存在暂时性死区。
• 示例:
console.log(a); // undefined(变量提升)
var a = 1;
2. 闭包
• 定义:函数能够访问并保留其词法作用域外的变量(如内部函数引用外部变量)。
• 应用:私有变量、防抖/节流函数。
• 风险:内存泄漏(未释放的 DOM 引用)。
三、原型链与继承
1. 原型链机制
• 原型对象:每个对象都有 __proto__ 指向其构造函数的 prototype,形成链式结构。
• 继承:通过 new 操作符创建实例,继承构造函数原型属性。
2. 手动实现继承
• 寄生组合继承(ES5):
function Child() { Parent.call(this); }
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
• ES6 继承:class Child extends Parent。
四、事件与异步
1. 事件流与事件委托
• 三个阶段:捕获 → 目标 → 冒泡。
• 示例:
<div οnclick="console.log('div')"><p οnclick="console.log('p')"></p></div>
点击 <p> 输出 p → div(冒泡阶段处理)。
2. 异步编程
• 事件循环顺序:宏任务(setTimeout)→ 微任务(Promise.then)→ UI 渲染。
• 手写 Promise.all:
function promiseAll(promises) {
return new Promise((resolve, reject) => {
let results = [], completed = 0;
promises.forEach((promise, i) => {
promise.then(res => {
results[i] = res;
if (++completed === promises.length) resolve(results);
}).catch(reject);
});
});
}
```[7](@ref)
五、手写核心函数
1. call/apply/bind 实现
• call:
Function.prototype.myCall = function(context, ...args) {
context = context || window;
context.fn = this;
const result = context.fn(...args);
delete context.fn;
return result;
};
```[7](@ref)
2. new 操作符原理
• 步骤:创建空对象 → 绑定原型 → 执行构造函数 → 返回对象。
• 手写 new:
function myNew(fn, ...args) {
const obj = Object.create(fn.prototype);
const result = fn.apply(obj, args);
return result instanceof Object ? result : obj;
}
```[7](@ref)
六、高频代码题
1. 深拷贝
function deepClone(obj, map = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj;
if (map.has(obj)) return map.get(obj);
const clone = Array.isArray(obj) ? [] : {};
map.set(obj, clone);
for (let key in obj) {
if (obj.hasOwnProperty(key)) clone[key] = deepClone(obj[key], map);
}
return clone;
}
```[3,7](@ref)
2. 数组去重
const unique = arr => [...new Set(arr)];
// 或
const unique = arr => arr.filter((item, i) => arr.indexOf(item) === i);
```[3](@ref)
总结与建议
以上题目覆盖了 数据类型、作用域、闭包、原型链、异步编程及手写核心函数 等高频考点。建议结合示例代码理解原理,并通过实际项目加深印象(如用闭包优化性能、手写 Promise 等)。更多完整题目可参考来源文档。
---
以下是 call、apply 和 bind 函数的手写实现及原理详解,结合了多个实现方案的优化思路:
1. 手写 call 函数
功能:立即调用函数,指定 this 指向并逐个传递参数。
实现步骤:
1. 检查调用者是否为函数(非函数调用需报错);
2. 处理上下文 context(若未传入则默认 window);
3. 为 context 添加唯一标识符(如 Symbol)避免属性覆盖;
4. 执行函数并保存结果,删除临时属性;
5. 返回执行结果。
Function.prototype.myCall = function(context, ...args) {
if (typeof this !== 'function') throw new TypeError('Not a function');
context = context || window;
const fnKey = Symbol('fn');
context[fnKey] = this;
const result = context[fnKey](...args);
delete context[fnKey];
return result;
};
示例:
function greet(greeting) { console.log(`${greeting}, ${this.name}`); }
const obj = { name: 'Alice' };
greet.myCall(obj, 'Hello'); // 输出 "Hello, Alice"
2. 手写 apply 函数
功能:与 call 类似,但参数以数组形式传递。
关键区别:
• 第二个参数需校验是否为数组(非数组需报错);
• 无参数时默认空数组。
Function.prototype.myApply = function(context, argsArray) {
if (typeof this !== 'function') throw new TypeError('Not a function');
context = context || window;
const fnKey = Symbol('fn');
context[fnKey] = this;
const result = argsArray?.length ? context[fnKey](...argsArray) : context[fnKey]();
delete context[fnKey];
return result;
};
示例:
function sum(a, b) { return a + b + this.offset; }
const context = { offset: 10 };
sum.myApply(context, [3, 5]); // 返回 18 (3+5+10)
3. 手写 bind 函数
功能:返回一个新函数,永久绑定 this 和预设参数,支持参数合并与构造函数调用。
核心逻辑:
1. 闭包保存原函数 fn、绑定上下文 context 和预设参数;
2. 返回的新函数需判断是否通过 new 调用(若是,则 this 指向新实例而非 context);
3. 合并预设参数与调用时参数。
Function.prototype.myBind = function(context, ...presetArgs) {
if (typeof this !== 'function') throw new TypeError('Not a function');
const fn = this;
const boundFn = function(...callArgs) {
// 判断是否为 new 调用(this 是否是 boundFn 的实例)
const isNewCall = this instanceof boundFn;
return fn.apply(isNewCall ? this : context, [...presetArgs, ...callArgs]);
};
// 继承原函数原型(解决 new 调用时的原型链问题)
boundFn.prototype = Object.create(fn.prototype);
return boundFn;
};
示例:
const obj = { x: 100 };
function logX(y) { console.log(this.x + y); }
const boundLog = logX.myBind(obj, 20);
boundLog(5); // 输出 120(100+20)
// new 调用场景
function Person(name) { this.name = name; }
const BoundPerson = Person.myBind({});
const p = new BoundPerson('Bob');
console.log(p.name); // 输出 "Bob"(this 指向新实例,而非空对象)
关键点总结
1. call/apply 差异:call 参数逐个传递,apply 参数为数组;
2. bind 的特殊性:
• 返回函数支持参数合并(柯里化);
• 处理 new 调用场景(忽略预设 this);
3. 错误处理:校验调用者类型,避免非函数调用;
4. 属性冲突:使用 Symbol 避免覆盖对象原有属性。
引用来源:
• call/apply 实现原理:
• bind 的构造函数处理:
• 参数合并与柯里化:
---
前端面试常考 LeetCode 题目详解
一、数组与字符串高频题
1. 两数之和(Two Sum)
• 题目描述:在数组中找到两个数,使它们的和等于目标值。
• 核心思路:使用哈希表(对象或 Map)存储已遍历元素的值和索引,将时间复杂度从 O(n²) 优化至 O(n)。
• 代码示例:
const twoSum = (nums, target) => {
const map = new Map();
for (let i = 0; i < nums.length; i++) {
const complement = target - nums[i];
if (map.has(complement)) return [map.get(complement), i];
map.set(nums[i], i);
}
};
2. 三数之和(3Sum)
• 题目描述:找出数组中所有不重复的三元组,使其和为 0。
• 核心思路:排序后使用双指针法,固定一个数,左右指针向中间逼近,跳过重复值避免重复解。
• 时间复杂度:O(n²),优于暴力解法的 O(n³)。
3. 最长无重复子串(Longest Substring Without Repeating Characters)
• 题目描述:寻找字符串中最长的不含重复字符的子串。
• 核心思路:滑动窗口 + 哈希表记录字符最后一次出现的位置,动态调整窗口左边界。
二、链表高频题
1. 反转链表(Reverse Linked List)
• 题目描述:将单链表反转。
• 核心思路:迭代法(三指针)或递归法,修改节点指向实现反转。
• 迭代示例:
const reverseList = (head) => {
let prev = null, curr = head;
while (curr) {
const next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}
return prev;
};
2. 环形链表(Linked List Cycle)
• 题目描述:判断链表是否有环。
• 核心思路:快慢指针法,快指针每次走两步,慢指针一步,相遇则有环。
3. 合并两个有序链表(Merge Two Sorted Lists)
• 题目描述:将两个升序链表合并为一个新链表。
• 核心思路:递归比较节点值,或迭代法构建虚拟头节点简化操作。
三、二叉树高频题
1. 二叉树层序遍历(Binary Tree Level Order Traversal)
• 题目描述:按层输出节点值。
• 核心思路:使用队列(BFS)逐层遍历,记录每层节点数。
• 代码示例:
const levelOrder = (root) => {
if (!root) return [];
const queue = [root], res = [];
while (queue.length) {
const level = [];
const size = queue.length;
for (let i = 0; i < size; i++) {
const node = queue.shift();
level.push(node.val);
if (node.left) queue.push(node.left);
if (node.right) queue.push(node.right);
}
res.push(level);
}
return res;
};
2. 验证二叉搜索树(Validate BST)
• 题目描述:判断二叉树是否为二叉搜索树。
• 核心思路:中序遍历结果为升序,或递归传递上下界验证节点值范围。
3. 二叉树的最大深度(Maximum Depth of Binary Tree)
• 题目描述:计算二叉树的最大深度。
• 核心思路:递归法(深度优先)或 BFS 层序遍历统计层数。
四、动态规划与回溯高频题
1. 爬楼梯(Climbing Stairs)
• 题目描述:每次爬 1 或 2 阶,求到达第 n 阶的方法数。
• 核心思路:动态规划,状态转移方程 dp[n] = dp[n-1] + dp[n-2]。
2. 打家劫舍(House Robber)
• 题目描述:不能偷相邻房屋,求最大收益。
• 核心思路:动态规划,状态转移方程 dp[i] = max(dp[i-1], dp[i-2] + nums[i])。
3. 全排列(Permutations)
• 题目描述:生成数组所有可能的排列。
• 核心思路:回溯法 + 剪枝,通过交换元素位置减少空间复杂度。
五、其他高频题型
1. 合并区间(Merge Intervals)
• 题目描述:合并所有重叠的区间。
• 核心思路:排序后遍历,比较当前区间与结果数组最后一个区间的结束位置。
2. LRU 缓存(LRU Cache)
• 题目描述:设计一个最近最少使用缓存。
• 核心思路:哈希表 + 双向链表实现 O(1) 的插入和删除。
总结与建议
1. 刷题策略:按类别集中突破(如先刷完所有链表题),避免零散刷题;
2. 核心思想:
• 空间换时间:哈希表、缓存机制(如 LRU);
• 双指针优化:滑动窗口、快慢指针;
3. 面试技巧:
• 先澄清题意(如边界条件、输入输出要求);
• 从暴力解法入手,逐步优化并解释思路。