JavaScript 对象生命周期
对象从创建到销毁的完整过程
问题
JavaScript 对象从创建到销毁经历了哪些阶段?垃圾回收是如何工作的?
解答
JavaScript 对象生命周期分为三个阶段:创建、使用、销毁。
1. 对象创建
// 字面量创建
const obj1 = { name: 'Alice' };
// 构造函数创建
const obj2 = new Object();
obj2.name = 'Bob';
// Object.create 创建(指定原型)
const proto = { greet() { return 'Hello'; } };
const obj3 = Object.create(proto);
// 类创建
class Person {
constructor(name) {
this.name = name;
}
}
const obj4 = new Person('Charlie');
创建时,引擎会:
- 在堆内存中分配空间
- 初始化内部属性(如
[[Prototype]]) - 执行构造逻辑
2. 对象使用
const user = { name: 'Alice', age: 25 };
// 读取属性
console.log(user.name); // 'Alice'
// 修改属性
user.age = 26;
// 添加属性
user.email = 'alice@example.com';
// 删除属性
delete user.age;
// 遍历属性
for (const key in user) {
console.log(key, user[key]);
}
3. 对象销毁(垃圾回收)
当对象不再被引用时,会被垃圾回收器回收。
// 引用计数的局限性
function createCycle() {
const objA = {};
const objB = {};
// 循环引用
objA.ref = objB;
objB.ref = objA;
// 函数结束后,objA 和 objB 互相引用
// 引用计数无法回收,但标记清除可以
}
createCycle();
// 现代引擎使用标记清除,能正确回收循环引用
标记清除算法
// 模拟标记清除过程
let root = {
child: {
grandchild: { value: 1 }
}
};
// 阶段1:从根对象开始,标记所有可达对象
// root -> child -> grandchild 都被标记
// 断开引用
root.child = null;
// 阶段2:grandchild 不可达,将被回收
// 下次 GC 时,grandchild 对象被清除
手动解除引用
class Cache {
constructor() {
this.data = new Map();
}
set(key, value) {
this.data.set(key, value);
}
// 清理方法,帮助 GC
clear() {
this.data.clear();
this.data = null;
}
}
let cache = new Cache();
cache.set('user', { name: 'Alice' });
// 使用完毕后清理
cache.clear();
cache = null; // 解除引用
WeakMap/WeakSet 的弱引用
// WeakMap 不阻止键对象被回收
const weakMap = new WeakMap();
let obj = { data: 'important' };
weakMap.set(obj, 'metadata');
console.log(weakMap.get(obj)); // 'metadata'
// 解除强引用
obj = null;
// obj 可被 GC 回收,weakMap 中的条目自动消失
// 适合存储对象的附加信息而不影响其生命周期
内存泄漏常见场景
// 1. 意外的全局变量
function leak1() {
leaked = 'oops'; // 没有声明,成为全局变量
}
// 2. 未清理的定时器
function leak2() {
const data = { /* 大量数据 */ };
setInterval(() => {
console.log(data); // data 永远不会被回收
}, 1000);
}
// 3. 闭包持有引用
function leak3() {
const largeData = new Array(1000000);
return function() {
// 即使不使用 largeData,闭包仍持有引用
console.log('hello');
};
}
// 4. DOM 引用未清理
const elements = [];
function leak4() {
const div = document.createElement('div');
elements.push(div); // 即使 DOM 移除,数组仍持有引用
}
关键点
- 创建阶段:堆内存分配空间,初始化原型链和内部属性
- 标记清除:现代引擎从根对象遍历,标记可达对象,清除不可达对象
- WeakMap/WeakSet:弱引用不阻止 GC,适合缓存和元数据存储
- 内存泄漏:全局变量、未清理的定时器、闭包、DOM 引用是常见原因
- 手动清理:大对象使用后设为 null,帮助 GC 更快回收
目录