JavaScript 核心特性综合实战 —— 从函数到对象的深度应用
这是一篇偏向中高级的实战向总结,重点在于把 JavaScript 最核心的几个概念(函数、闭包、this、原型、对象、ES6+ 新特性)通过真实场景代码串联起来,帮助你真正“用起来”而不是只停留在理论。
一、核心概念速览(带着问题阅读)
| 概念 | 关键一句话理解 | 实战中最容易出错的地方 | 现代解决方案 / 推荐写法 |
|---|---|---|---|
| 函数是一等公民 | 可以赋值、传参、返回 | this 指向混乱 | 箭头函数 + 明确绑定 |
| 闭包 | 函数 + 它能访问的外部词法环境 | 内存泄漏、循环中的闭包陷阱 | 模块模式、IIFE、块级作用域 + let/const |
| this | 动态绑定,取决于调用方式 | 事件回调、setTimeout、对象方法丢失 this | 箭头函数、bind、class + 自动绑定 |
| 原型与继承 | 对象通过 __proto__ 链接到原型链 | 原型污染、for…in 遍历到原型属性 | Object.create、class、组合优于继承 |
| 对象字面量 | 最常用的创建对象方式 | 属性名冲突、方法中 this 丢失 | 简写语法、计算属性名、Symbol |
| ES6+ 对象增强 | 解构、展开、Object.assign、Object.fromEntries | 浅拷贝问题、属性描述符丢失 | 结构化克隆、深拷贝库、Proxy |
二、综合实战案例:实现一个“任务管理器”模块
我们通过一个小型任务管理系统,把上面所有核心特性串起来。
// =============================================
// 1. 使用闭包 + 模块模式 封装私有数据
// =============================================
const createTaskManager = () => {
// 私有变量(外部无法直接访问)
let tasks = [];
let taskIdCounter = 0;
const listeners = new Set(); // 观察者模式 - 事件订阅
// 私有辅助函数
const generateId = () => ++taskIdCounter;
// 对外暴露的 API 对象
return {
// 添加任务(闭包访问私有变量)
addTask(title, priority = 1) {
const task = {
id: generateId(),
title,
priority,
completed: false,
createdAt: new Date(),
};
tasks.push(task);
this.notify('taskAdded', task);
return task;
},
// 删除任务(使用 findIndex + splice)
removeTask(id) {
const index = tasks.findIndex(t => t.id === id);
if (index !== -1) {
const removed = tasks.splice(index, 1)[0];
this.notify('taskRemoved', removed);
return removed;
}
return null;
},
// 切换完成状态(对象方法中的 this)
toggleComplete(id) {
const task = tasks.find(t => t.id === id);
if (task) {
task.completed = !task.completed;
this.notify('taskToggled', task);
}
},
// 获取所有任务(返回副本防止外部修改原数据)
getAllTasks() {
return [...tasks]; // 浅拷贝
},
// 按优先级排序(函数作为参数)
getSortedTasks(sortBy = 'priority') {
return [...tasks].sort((a, b) => {
if (sortBy === 'priority') return b.priority - a.priority;
if (sortBy === 'created') return b.createdAt - a.createdAt;
return 0;
});
},
// 订阅事件(闭包保存 listeners)
subscribe(callback) {
listeners.add(callback);
return () => listeners.delete(callback); // 返回取消订阅函数
},
// 通知所有订阅者
notify(event, data) {
listeners.forEach(cb => cb(event, data));
}
};
};
// =============================================
// 2. 使用方式 - 体现 this、闭包、原型链
// =============================================
const manager = createTaskManager();
// 订阅变化(闭包 + 高阶函数)
const unsubscribe = manager.subscribe((event, task) => {
console.log(`事件:${event}`, task);
renderTasks(); // 假设有 UI 更新函数
});
// 添加任务
manager.addTask("完成周报", 3);
manager.addTask("修复 Bug #123", 1);
manager.toggleComplete(1);
// 3秒后自动移除高优先级任务(this 丢失经典场景)
setTimeout(() => {
// 错误写法:this 指向 window / undefined
// manager.getAllTasks().forEach(task => {
// if (task.priority === 1) manager.removeTask(task.id);
// });
// 正确写法1:箭头函数保留 this
manager.getAllTasks().forEach(task => {
if (task.priority === 1) manager.removeTask(task.id);
});
// 正确写法2:bind
// manager.getAllTasks().forEach(function(task) {
// if (task.priority === 1) this.removeTask(task.id);
// }.bind(manager));
}, 3000);
// 取消订阅
// unsubscribe();
// =============================================
// 3. 扩展:使用 class 语法 + 原型继承风格
// =============================================
class AdvancedTaskManager extends Object {
constructor() {
super();
this.tasks = [];
this.idCounter = 0;
this.listeners = new Set();
}
addTask(title, priority = 1) {
const task = {
id: ++this.idCounter,
title,
priority,
completed: false,
createdAt: new Date(),
// 方法定义简写 + this 自动绑定(class 特性)
getInfo() {
return `${this.title} (优先级 ${this.priority})`;
}
};
this.tasks.push(task);
this.notify('taskAdded', task);
return task;
}
// ... 其他方法类似
}
// =============================================
// 4. 现代写法对比:使用 Proxy 实现响应式任务
// =============================================
function reactiveTasks(initialTasks = []) {
const handlers = {
set(target, prop, value) {
const result = Reflect.set(target, prop, value);
console.log(`任务列表变化:${prop} 被修改`);
// 可以在这里触发 UI 更新
return result;
}
};
return new Proxy(initialTasks, handlers);
}
const reactiveList = reactiveTasks();
reactiveList.push({ id: 1, title: "测试 Proxy" }); // 会自动打印变化
三、核心考察点总结(面试/实战常问)
- 闭包最经典的两个问题
- for var i 循环中的 setTimeout
- 内存泄漏(未释放的引用)
- this 绑定规则优先级(ES6 前)
- new 绑定
- 显式绑定(call/apply/bind)
- 隐式绑定(obj.method())
- 默认绑定(window / undefined)
- 对象拷贝的多种方式及深浅区别
{ ...obj }/Object.assign()→ 浅拷贝structuredClone()(现代浏览器推荐)JSON.parse(JSON.stringify(obj))→ 局限性大- lodash / clone-deep 等库
- 如何实现一个简易的继承?
- Object.create + Object.assign
- class + extends + super
- 组合寄生式继承(最推荐)
四、进阶练习题(建议动手写)
- 用闭包实现一个计数器(支持 increment、decrement、reset)
- 用 Proxy 实现一个简单的双向绑定(类似 Vue2 的响应式)
- 实现一个
new操作符的模拟函数 - 用 ES6+ 语法重写一个“经典 for var i 的 setTimeout 闭包问题”
如果你想针对上面某一个点深入展开代码实现、或者需要某个具体场景的完整代码(防抖节流、发布订阅、深拷贝、事件委托等),可以直接告诉我,我会继续提供详细写法和原理说明。