JavaScript 基础与进阶
JavaScript 是前端开发的核心语言。本章节包含基础概念、进阶特性以及大量实战面试题。
专题模块
- 数据结构 (Data Structures): 链表、树、栈、队列等 JS 实现。
- 算法与实战 (Algorithms): 排序、查找、去重、扁平化等常用算法。
- 设计模式 (Design Patterns): 单例、观察者、工厂等模式。
- 异步编程 (Async): Promise、Async/Await 手写实现与原理。
- 函数式编程 (Functional Programming): 纯函数、柯里化、FP 概念。
- 面向对象与原型 (OOP): 继承、类、原型链。
- 性能与 DOM (Performance): 防抖节流、懒加载、DOM 优化。
面试题精选
1. 闭包(Closure)
- 题目:请解释什么是闭包?并写一个计数器函数示例。
- 考点:作用域链、函数执行上下文、内存管理。
参考答案:
闭包是指函数能访问并记住其词法作用域,即使该函数在其作用域之外执行。
javascript
function counter() {
let count = 0;
return function () {
return ++count;
};
}
const inc = counter();
console.log(inc()); // 1
console.log(inc()); // 22. 原型链与继承
- 题目:JavaScript 中如何实现继承?原型链有什么作用?
- 考点:原型对象、
__proto__、Object.create、ES6 class。
参考答案:
JS 中对象通过 [[prototype]](即 __proto__)属性连接形成原型链,实现继承和属性共享。所有对象最终继承自 Object.prototype。终点是 null。
3. this 指向
- 题目:解释
this在不同场景下的指向,包括普通函数、箭头函数、事件处理、call/apply/bind。 - 考点:执行上下文、绑定规则。
参考答案:
this 指向取决于函数调用方式。
- 普通函数默认指向全局对象(严格模式下为
undefined); - 方法调用指向调用该方法的对象;
- 构造函数指向新创建的实例;
- 箭头函数没有自己的
this,继承自外层作用域; call/apply/bind可以显式设置this。
优先级:
new绑定- 显式绑定(
call/apply/bind) - 隐式绑定(对象方法调用)
- 默认绑定(普通函数调用,严格模式下为
undefined) - 箭头函数继承外层
this。
4. 事件循环与异步机制
- 题目:解释 JS 的事件循环(Event Loop)和微任务(Microtask)与宏任务(Macrotask)的区别。
- 考点:Promise、setTimeout、MutationObserver。
参考答案:
JavaScript 是单线程的,事件循环是 JS 处理异步操作的机制。
- 宏任务:包括
setTimeout、setInterval、I/O 操作等。 - 微任务:包括
Promise.then、MutationObserver等。
每次事件循环会先执行所有微任务队列,然后再执行一个宏任务。
执行顺序:
- 执行全局代码(同步任务)
- 执行微任务队列 (
Promise.then/MutationObserver) - 执行一个宏任务 (
setTimeout/ I/O 操作) - 重复步骤 2 和 3。
口诀:同步 → 微任务 → 宏任务
5. 深浅拷贝
- 题目:写出深拷贝和浅拷贝的实现方式,并说明各自区别。
- 考点:对象引用、循环引用、JSON 方法、递归实现。
参考答案:
区别:浅拷贝只复制对象的第一层属性,深拷贝会递归复制所有层级的属性。
javascript
// --- 深拷贝 ---
// 方式1: JSON (不支持函数、undefined、Symbol等)
const deepCloneJSON = JSON.parse(JSON.stringify(target));
// 方式2: structuredClone (现代浏览器支持)
const deepCloneStructured = structuredClone(target);
// 方式3: 递归实现
function deepClone(target) {
if (typeof target !== "object" || target === null) {
return target;
}
let cloneTarget = Array.isArray(target) ? [] : {};
for (let key in target) {
if (target.hasOwnProperty(key)) {
cloneTarget[key] = deepClone(target[key]);
}
}
return cloneTarget;
}
// --- 浅拷贝 ---
// 方式1: Object.assign
const shallowClone1 = Object.assign({}, target);
// 方式2: 扩展运算符
const shallowClone2 = { ...target };
// 方式3: 手动实现
function shallowClone3(target) {
if (typeof target !== "object" || target === null) {
return target;
}
let cloneTarget = Array.isArray(target) ? [] : {};
for (let key in target) {
if (target.hasOwnProperty(key)) {
cloneTarget[key] = target[key];
}
}
return cloneTarget;
}6. 数组去重、排序、求和
- 题目:实现数组去重、数组求和、数组中两个数和等于目标值的算法。
- 考点:Set、Map、双指针、reduce。
参考答案:
javascript
// 1. 数组去重
function uniqueArray(arr) {
return [...new Set(arr)];
}
// 2. 数组求和
const sumArray = (arr) => arr.reduce((total, val) => total + val, 0);
// 3. 数组中两数和等于目标值 (Two Sum)
function twoSum(arr, target) {
const mp = new Map();
for (let i = 0; i < arr.length; i++) {
const diff = target - arr[i];
if (mp.has(diff)) {
return [mp.get(diff), i];
}
mp.set(arr[i], i);
}
return [];
}7. 模块化
- 题目:ESM 和 CommonJS 的区别,为什么 Tree Shaking 只支持 ESM。
- 考点:静态分析、模块加载机制。
参考答案:
ESM (ECMAScript Modules)
- JavaScript 的原生模块系统。
- 静态分析:允许在编译时确定依赖关系,从而实现 Tree Shaking(去除未使用代码)。
- 语法:使用
import/export。 - 加载方式:静态加载。
- 执行时机:编译时解析。
- this 指向:顶层的
this是undefined。 - 适用场景:前端和现代 JavaScript 项目。
CommonJS
- Node.js 采用的模块系统。
- 动态加载:无法在编译时确定依赖关系,因此 不支持 Tree Shaking。
- 语法:使用
require/module.exports。 - 加载方式:动态加载。
- 执行时机:运行时解析。
- this 指向:顶层的
this指向module.exports。 - 适用场景:主要用于 Node.js 环境。