JavaScript|第9章:对象 — 基础

JavaScript|第9章:对象 --- 基础

对象基础

  • [JavaScript|第9章:对象 --- 基础](#JavaScript|第9章:对象 — 基础)
    • 引言
    • [9.1 对象的创建方式](#9.1 对象的创建方式)
      • [9.1.1 对象字面量(Object Literal)](#9.1.1 对象字面量(Object Literal))
      • [9.1.2 new Object() 构造函数](#9.1.2 new Object() 构造函数)
      • [9.1.3 两种方式的对比](#9.1.3 两种方式的对比)
    • [9.2 属性的增删改查](#9.2 属性的增删改查)
      • [9.2.1 读取属性](#9.2.1 读取属性)
      • [9.2.2 添加与修改属性](#9.2.2 添加与修改属性)
      • [9.2.3 删除属性:delete 操作符](#9.2.3 删除属性:delete 操作符)
      • [9.2.4 检查属性是否存在:in 操作符](#9.2.4 检查属性是否存在:in 操作符)
      • [9.2.5 遍历对象属性](#9.2.5 遍历对象属性)
    • [9.3 对象引用与复制](#9.3 对象引用与复制)
      • [9.3.1 原始类型 vs 引用类型](#9.3.1 原始类型 vs 引用类型)
      • [9.3.2 比较:引用相等](#9.3.2 比较:引用相等)
      • [9.3.3 浅拷贝(Shallow Copy)](#9.3.3 浅拷贝(Shallow Copy))
      • [9.3.4 深拷贝(Deep Copy)](#9.3.4 深拷贝(Deep Copy))
      • [9.3.5 structuredClone(现代方案)](#9.3.5 structuredClone(现代方案))
    • [9.4 垃圾回收机制基础](#9.4 垃圾回收机制基础)
      • [9.4.1 可达性(Reachability)](#9.4.1 可达性(Reachability))
      • [9.4.2 垃圾回收算法:标记-清除](#9.4.2 垃圾回收算法:标记-清除)
      • [9.4.3 常见内存泄漏场景](#9.4.3 常见内存泄漏场景)
    • [9.5 Object 静态方法](#9.5 Object 静态方法)
      • [9.5.1 Object.keys / values / entries](#9.5.1 Object.keys / values / entries)
      • [9.5.2 Object.assign:对象合并](#9.5.2 Object.assign:对象合并)
      • [9.5.3 Object.freeze / seal / preventExtensions](#9.5.3 Object.freeze / seal / preventExtensions)
      • [9.5.4 其他常用 Object 方法](#9.5.4 其他常用 Object 方法)
    • [9.6 实战:对象合并与深拷贝实现](#9.6 实战:对象合并与深拷贝实现)
      • [9.6.1 实战1:智能对象合并](#9.6.1 实战1:智能对象合并)
      • [9.6.2 实战2:手写深拷贝](#9.6.2 实战2:手写深拷贝)
      • [9.6.3 深拷贝实现要点解析](#9.6.3 深拷贝实现要点解析)
    • 本章小结

上篇回顾第8章:代码质量

下篇预告第10章:对象方法与 this


引言

在前面的章节中,我们学习了原始类型(numberstringboolean 等)。原始类型只能存储单个值,但现实世界的数据往往是结构化的------一个用户有姓名、年龄、邮箱、地址......用一堆独立的变量来管理这些数据,既不直观,也难以维护。

对象(Object)是 JavaScript 中最核心的数据结构。它将相关的数据和行为组织在一起,是构建复杂程序的基石。事实上,JavaScript 中几乎所有东西都是对象------数组是对象、函数是对象、甚至日期也是对象。
#mermaid-svg-3YEU9aPzmx8yLdn6{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-3YEU9aPzmx8yLdn6 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-3YEU9aPzmx8yLdn6 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-3YEU9aPzmx8yLdn6 .error-icon{fill:#552222;}#mermaid-svg-3YEU9aPzmx8yLdn6 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-3YEU9aPzmx8yLdn6 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-3YEU9aPzmx8yLdn6 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-3YEU9aPzmx8yLdn6 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-3YEU9aPzmx8yLdn6 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-3YEU9aPzmx8yLdn6 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-3YEU9aPzmx8yLdn6 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-3YEU9aPzmx8yLdn6 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-3YEU9aPzmx8yLdn6 .marker.cross{stroke:#333333;}#mermaid-svg-3YEU9aPzmx8yLdn6 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-3YEU9aPzmx8yLdn6 p{margin:0;}#mermaid-svg-3YEU9aPzmx8yLdn6 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-3YEU9aPzmx8yLdn6 .cluster-label text{fill:#333;}#mermaid-svg-3YEU9aPzmx8yLdn6 .cluster-label span{color:#333;}#mermaid-svg-3YEU9aPzmx8yLdn6 .cluster-label span p{background-color:transparent;}#mermaid-svg-3YEU9aPzmx8yLdn6 .label text,#mermaid-svg-3YEU9aPzmx8yLdn6 span{fill:#333;color:#333;}#mermaid-svg-3YEU9aPzmx8yLdn6 .node rect,#mermaid-svg-3YEU9aPzmx8yLdn6 .node circle,#mermaid-svg-3YEU9aPzmx8yLdn6 .node ellipse,#mermaid-svg-3YEU9aPzmx8yLdn6 .node polygon,#mermaid-svg-3YEU9aPzmx8yLdn6 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-3YEU9aPzmx8yLdn6 .rough-node .label text,#mermaid-svg-3YEU9aPzmx8yLdn6 .node .label text,#mermaid-svg-3YEU9aPzmx8yLdn6 .image-shape .label,#mermaid-svg-3YEU9aPzmx8yLdn6 .icon-shape .label{text-anchor:middle;}#mermaid-svg-3YEU9aPzmx8yLdn6 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-3YEU9aPzmx8yLdn6 .rough-node .label,#mermaid-svg-3YEU9aPzmx8yLdn6 .node .label,#mermaid-svg-3YEU9aPzmx8yLdn6 .image-shape .label,#mermaid-svg-3YEU9aPzmx8yLdn6 .icon-shape .label{text-align:center;}#mermaid-svg-3YEU9aPzmx8yLdn6 .node.clickable{cursor:pointer;}#mermaid-svg-3YEU9aPzmx8yLdn6 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-3YEU9aPzmx8yLdn6 .arrowheadPath{fill:#333333;}#mermaid-svg-3YEU9aPzmx8yLdn6 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-3YEU9aPzmx8yLdn6 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-3YEU9aPzmx8yLdn6 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3YEU9aPzmx8yLdn6 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-3YEU9aPzmx8yLdn6 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3YEU9aPzmx8yLdn6 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-3YEU9aPzmx8yLdn6 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-3YEU9aPzmx8yLdn6 .cluster text{fill:#333;}#mermaid-svg-3YEU9aPzmx8yLdn6 .cluster span{color:#333;}#mermaid-svg-3YEU9aPzmx8yLdn6 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-3YEU9aPzmx8yLdn6 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-3YEU9aPzmx8yLdn6 rect.text{fill:none;stroke-width:0;}#mermaid-svg-3YEU9aPzmx8yLdn6 .icon-shape,#mermaid-svg-3YEU9aPzmx8yLdn6 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3YEU9aPzmx8yLdn6 .icon-shape p,#mermaid-svg-3YEU9aPzmx8yLdn6 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-3YEU9aPzmx8yLdn6 .icon-shape .label rect,#mermaid-svg-3YEU9aPzmx8yLdn6 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3YEU9aPzmx8yLdn6 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-3YEU9aPzmx8yLdn6 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-3YEU9aPzmx8yLdn6 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} JavaScript 数据类型
原始类型
引用类型(对象)
number / string / boolean
null / undefined
symbol / bigint
普通对象 {}
数组 \[\]
函数 function
Date / RegExp / Map / Set

本章目标

  • 掌握对象的两种创建方式:字面量与构造函数
  • 熟练进行属性的增删改查,理解 indelete 操作符
  • 理解对象的引用本质,区分浅拷贝与深拷贝
  • 了解 JavaScript 的垃圾回收机制
  • 掌握 Object 常用静态方法
  • 实战:实现一个支持嵌套对象的深拷贝函数

9.1 对象的创建方式

JavaScript 提供了多种创建对象的方式,最常用的是对象字面量new Object() 构造函数。

9.1.1 对象字面量(Object Literal)

对象字面量是最直观、最推荐的创建方式,用花括号 {} 包裹键值对。

javascript 复制代码
// 对象字面量
const user = {
  name: 'Alice',
  age: 25,
  email: 'alice@example.com',
  isActive: true
};

console.log(user.name);   // "Alice"
console.log(user.age);    // 25

属性名(Key)的规则

javascript 复制代码
const obj = {
  name: 'Alice',         // 合法的标识符 → 不需要引号
  'first name': '张',    // 包含空格 → 必须加引号
  '2024-year': true,     // 以数字开头 → 必须加引号
  '': '空字符串键',       // 空字符串也可以作为键
  [Symbol()]: 'Symbol键' // Symbol 作为键
};

计算属性名(ES6):

javascript 复制代码
const field = 'email';

const user = {
  name: 'Alice',
  [field]: 'alice@example.com'  // 动态计算属性名
};

console.log(user.email); // "alice@example.com"

// 更复杂的计算
const prefix = 'user';
const config = {
  [`${prefix}Name`]: 'Alice',   // → userName
  [`${prefix}Age`]: 25          // → userAge
};

属性简写(ES6):当变量名和属性名相同时,可以省略冒号和值。

javascript 复制代码
const name = 'Alice';
const age = 25;

// 简写(推荐)
const user = { name, age };

// 等价于
const user = { name: name, age: age };

9.1.2 new Object() 构造函数

javascript 复制代码
const user = new Object();
user.name = 'Alice';
user.age = 25;

console.log(user); // { name: 'Alice', age: 25 }

不推荐new Object() 写法冗长,且没有字面量直观。在实际开发中几乎总是使用字面量。

9.1.3 两种方式的对比

特性 对象字面量 {} new Object()
语法简洁度 ✅ 简洁直观 ❌ 冗长
性能 ✅ 引擎优化更好 略慢(需要调用构造函数)
可读性 ✅ 一眼看出结构 ❌ 需要逐行阅读
动态属性 支持计算属性名 需要赋值语句
推荐程度 ✅ 强烈推荐 ❌ 不推荐
javascript 复制代码
// ✅ 推荐:字面量
const product = {
  name: 'MacBook Pro',
  price: 14999,
  specs: { cpu: 'M3', ram: '16GB' }
};

// ❌ 不推荐:new Object()
const product = new Object();
product.name = 'MacBook Pro';
product.price = 14999;
product.specs = new Object();
product.specs.cpu = 'M3';
product.specs.ram = '16GB';

9.2 属性的增删改查

对象创建之后,我们经常需要读取、添加、修改和删除属性。

9.2.1 读取属性

JavaScript 提供了两种读取属性的方式:点号(.方括号([]

javascript 复制代码
const user = {
  name: 'Alice',
  age: 25,
  'full address': '北京市朝阳区'
};

// 点号访问(推荐,简洁)
console.log(user.name); // "Alice"

// 方括号访问(动态键或含特殊字符的键)
console.log(user['name']);           // "Alice"
console.log(user['full address']);   // "北京市朝阳区"

// 方括号支持动态计算
const field = 'age';
console.log(user[field]); // 25

// ❌ 点号不支持动态计算
// console.log(user.field); // undefined(查找名为 "field" 的属性)

访问不存在的属性返回 undefined

javascript 复制代码
const user = { name: 'Alice' };

console.log(user.age);    // undefined(不是报错)
console.log(user.email);  // undefined

注意 :访问不存在的属性不会报错,而是静默返回 undefined。这是 JavaScript 对象的一个重要特点,也是很多 Bug 的来源。

9.2.2 添加与修改属性

javascript 复制代码
const user = { name: 'Alice' };

// 添加新属性
user.age = 25;
user.email = 'alice@example.com';

// 修改已有属性
user.name = 'Alice Wang';

console.log(user);
// { name: 'Alice Wang', age: 25, email: 'alice@example.com' }

批量添加属性 (使用展开运算符或 Object.assign):

javascript 复制代码
const user = { name: 'Alice' };
const updates = { age: 25, email: 'alice@example.com' };

// 方式1:Object.assign
Object.assign(user, updates);

// 方式2:展开运算符(创建新对象)
const newUser = { ...user, ...updates };

9.2.3 删除属性:delete 操作符

javascript 复制代码
const user = {
  name: 'Alice',
  age: 25,
  email: 'alice@example.com'
};

delete user.email;
console.log(user); // { name: 'Alice', age: 25 }
console.log(user.email); // undefined

注意delete 只能删除对象自身的属性 ,不能删除变量或函数声明。delete 操作成功返回 true,失败返回 false(如删除不可配置的属性)。

javascript 复制代码
// delete 的返回值
const obj = { a: 1 };
console.log(delete obj.a);       // true(删除成功)
console.log(delete obj.a);       // true(属性不存在也算成功)
console.log(delete obj.unknown); // true

9.2.4 检查属性是否存在:in 操作符

由于访问不存在的属性返回 undefined,我们不能用 === undefined 来判断属性是否存在(属性可能存在但值就是 undefined)。in 操作符是更可靠的方式。

javascript 复制代码
const user = { name: 'Alice', age: 25, hobby: undefined };

// ✅ in 操作符:检查属性是否存在(包括原型链上的属性)
console.log('name' in user);   // true
console.log('age' in user);    // true
console.log('hobby' in user);  // true(属性存在,值为 undefined)
console.log('email' in user);  // false(属性不存在)

// ❌ 不推荐:用 undefined 判断
console.log(user.hobby !== undefined);  // false(但 hobby 属性确实存在)
console.log(user.email !== undefined);  // false(email 属性确实不存在)

hasOwnProperty:只检查自身属性(不含原型链)

javascript 复制代码
const user = { name: 'Alice' };

console.log('name' in user);               // true
console.log('toString' in user);            // true(来自 Object.prototype)
console.log(user.hasOwnProperty('name'));     // true
console.log(user.hasOwnProperty('toString')); // false(不是自身属性)

// ES2022 推荐写法:Object.hasOwn
console.log(Object.hasOwn(user, 'name'));     // true
console.log(Object.hasOwn(user, 'toString')); // false
方法 检查自身属性 检查原型链属性 推荐度
in 需要了解原型链时使用
hasOwnProperty 旧代码常见
Object.hasOwn ✅ 现代推荐写法

9.2.5 遍历对象属性

javascript 复制代码
const user = { name: 'Alice', age: 25, city: '北京' };

// for...in 遍历所有可枚举属性(包括原型链)
for (const key in user) {
  if (Object.hasOwn(user, key)) {  // 过滤原型链属性
    console.log(`${key}: ${user[key]}`);
  }
}

// Object.keys() 只获取自身可枚举属性名
console.log(Object.keys(user));   // ['name', 'age', 'city']

// Object.values() 只获取属性值
console.log(Object.values(user)); // ['Alice', 25, '北京']

// Object.entries() 获取键值对数组
for (const [key, value] of Object.entries(user)) {
  console.log(`${key} = ${value}`);
}

属性顺序 :对象的属性遍历顺序有规则------整数键 按升序排列,字符串键Symbol 键按插入顺序排列。

javascript 复制代码
const obj = {
  b: 2,
  a: 1,
  3: 'three',
  1: 'one',
  c: 3
};

console.log(Object.keys(obj));
// ['1', '3', 'b', 'a', 'c']
// 整数键 1, 3 按升序,字符串键 b, a, c 按插入顺序

9.3 对象引用与复制

理解对象的引用本质是避免 Bug 的关键。原始类型和引用类型在赋值时的行为完全不同。

9.3.1 原始类型 vs 引用类型

javascript 复制代码
// 原始类型:赋值时复制值
let a = 10;
let b = a;
b = 20;
console.log(a); // 10(a 不受影响)
console.log(b); // 20

// 引用类型:赋值时复制引用(指向同一对象)
const user1 = { name: 'Alice', age: 25 };
const user2 = user1;        // user2 和 user1 指向同一个对象
user2.name = 'Bob';
console.log(user1.name);    // "Bob"(user1 也被修改了!)
console.log(user2.name);    // "Bob"

#mermaid-svg-0QMK5Fhwy5XJLNap{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-0QMK5Fhwy5XJLNap .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-0QMK5Fhwy5XJLNap .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-0QMK5Fhwy5XJLNap .error-icon{fill:#552222;}#mermaid-svg-0QMK5Fhwy5XJLNap .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-0QMK5Fhwy5XJLNap .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-0QMK5Fhwy5XJLNap .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-0QMK5Fhwy5XJLNap .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-0QMK5Fhwy5XJLNap .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-0QMK5Fhwy5XJLNap .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-0QMK5Fhwy5XJLNap .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-0QMK5Fhwy5XJLNap .marker{fill:#333333;stroke:#333333;}#mermaid-svg-0QMK5Fhwy5XJLNap .marker.cross{stroke:#333333;}#mermaid-svg-0QMK5Fhwy5XJLNap svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-0QMK5Fhwy5XJLNap p{margin:0;}#mermaid-svg-0QMK5Fhwy5XJLNap .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-0QMK5Fhwy5XJLNap .cluster-label text{fill:#333;}#mermaid-svg-0QMK5Fhwy5XJLNap .cluster-label span{color:#333;}#mermaid-svg-0QMK5Fhwy5XJLNap .cluster-label span p{background-color:transparent;}#mermaid-svg-0QMK5Fhwy5XJLNap .label text,#mermaid-svg-0QMK5Fhwy5XJLNap span{fill:#333;color:#333;}#mermaid-svg-0QMK5Fhwy5XJLNap .node rect,#mermaid-svg-0QMK5Fhwy5XJLNap .node circle,#mermaid-svg-0QMK5Fhwy5XJLNap .node ellipse,#mermaid-svg-0QMK5Fhwy5XJLNap .node polygon,#mermaid-svg-0QMK5Fhwy5XJLNap .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-0QMK5Fhwy5XJLNap .rough-node .label text,#mermaid-svg-0QMK5Fhwy5XJLNap .node .label text,#mermaid-svg-0QMK5Fhwy5XJLNap .image-shape .label,#mermaid-svg-0QMK5Fhwy5XJLNap .icon-shape .label{text-anchor:middle;}#mermaid-svg-0QMK5Fhwy5XJLNap .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-0QMK5Fhwy5XJLNap .rough-node .label,#mermaid-svg-0QMK5Fhwy5XJLNap .node .label,#mermaid-svg-0QMK5Fhwy5XJLNap .image-shape .label,#mermaid-svg-0QMK5Fhwy5XJLNap .icon-shape .label{text-align:center;}#mermaid-svg-0QMK5Fhwy5XJLNap .node.clickable{cursor:pointer;}#mermaid-svg-0QMK5Fhwy5XJLNap .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-0QMK5Fhwy5XJLNap .arrowheadPath{fill:#333333;}#mermaid-svg-0QMK5Fhwy5XJLNap .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-0QMK5Fhwy5XJLNap .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-0QMK5Fhwy5XJLNap .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-0QMK5Fhwy5XJLNap .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-0QMK5Fhwy5XJLNap .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-0QMK5Fhwy5XJLNap .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-0QMK5Fhwy5XJLNap .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-0QMK5Fhwy5XJLNap .cluster text{fill:#333;}#mermaid-svg-0QMK5Fhwy5XJLNap .cluster span{color:#333;}#mermaid-svg-0QMK5Fhwy5XJLNap div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-0QMK5Fhwy5XJLNap .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-0QMK5Fhwy5XJLNap rect.text{fill:none;stroke-width:0;}#mermaid-svg-0QMK5Fhwy5XJLNap .icon-shape,#mermaid-svg-0QMK5Fhwy5XJLNap .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-0QMK5Fhwy5XJLNap .icon-shape p,#mermaid-svg-0QMK5Fhwy5XJLNap .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-0QMK5Fhwy5XJLNap .icon-shape .label rect,#mermaid-svg-0QMK5Fhwy5XJLNap .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-0QMK5Fhwy5XJLNap .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-0QMK5Fhwy5XJLNap .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-0QMK5Fhwy5XJLNap :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 指向
指向
user1
内存中的对象

name: 'Alice', age: 25
user2

关键理解 :对象变量存储的不是对象本身,而是对象在内存中的地址(引用)。赋值操作只是复制了地址,两个变量仍然指向同一块内存。

9.3.2 比较:引用相等

javascript 复制代码
// 原始类型:值相等即为 true
console.log(1 === 1);     // true

// 引用类型:必须是同一个对象才为 true
const a = { name: 'Alice' };
const b = { name: 'Alice' };
const c = a;

console.log(a === b); // false(不同对象,即使内容完全相同)
console.log(a === c); // true(同一对象的不同引用)

9.3.3 浅拷贝(Shallow Copy)

浅拷贝创建一个新对象,但只复制第一层属性。如果属性值本身是对象,复制的仍然是引用。

javascript 复制代码
// 方式1:展开运算符
const original = { name: 'Alice', address: { city: '北京' } };
const shallow = { ...original };

shallow.name = 'Bob';           // 修改原始类型 → 不影响原对象
shallow.address.city = '上海';  // 修改嵌套对象 → 原对象也被影响!

console.log(original.name);           // "Alice"(未受影响)
console.log(original.address.city);   // "上海"(被影响了)

// 方式2:Object.assign
const shallow2 = Object.assign({}, original);

// 方式3:手动复制
const shallow3 = {};
for (const key in original) {
  shallow3[key] = original[key];
}

#mermaid-svg-BK1OZsSgxy0b3xQR{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-BK1OZsSgxy0b3xQR .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-BK1OZsSgxy0b3xQR .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-BK1OZsSgxy0b3xQR .error-icon{fill:#552222;}#mermaid-svg-BK1OZsSgxy0b3xQR .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-BK1OZsSgxy0b3xQR .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-BK1OZsSgxy0b3xQR .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-BK1OZsSgxy0b3xQR .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-BK1OZsSgxy0b3xQR .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-BK1OZsSgxy0b3xQR .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-BK1OZsSgxy0b3xQR .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-BK1OZsSgxy0b3xQR .marker{fill:#333333;stroke:#333333;}#mermaid-svg-BK1OZsSgxy0b3xQR .marker.cross{stroke:#333333;}#mermaid-svg-BK1OZsSgxy0b3xQR svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-BK1OZsSgxy0b3xQR p{margin:0;}#mermaid-svg-BK1OZsSgxy0b3xQR .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-BK1OZsSgxy0b3xQR .cluster-label text{fill:#333;}#mermaid-svg-BK1OZsSgxy0b3xQR .cluster-label span{color:#333;}#mermaid-svg-BK1OZsSgxy0b3xQR .cluster-label span p{background-color:transparent;}#mermaid-svg-BK1OZsSgxy0b3xQR .label text,#mermaid-svg-BK1OZsSgxy0b3xQR span{fill:#333;color:#333;}#mermaid-svg-BK1OZsSgxy0b3xQR .node rect,#mermaid-svg-BK1OZsSgxy0b3xQR .node circle,#mermaid-svg-BK1OZsSgxy0b3xQR .node ellipse,#mermaid-svg-BK1OZsSgxy0b3xQR .node polygon,#mermaid-svg-BK1OZsSgxy0b3xQR .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-BK1OZsSgxy0b3xQR .rough-node .label text,#mermaid-svg-BK1OZsSgxy0b3xQR .node .label text,#mermaid-svg-BK1OZsSgxy0b3xQR .image-shape .label,#mermaid-svg-BK1OZsSgxy0b3xQR .icon-shape .label{text-anchor:middle;}#mermaid-svg-BK1OZsSgxy0b3xQR .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-BK1OZsSgxy0b3xQR .rough-node .label,#mermaid-svg-BK1OZsSgxy0b3xQR .node .label,#mermaid-svg-BK1OZsSgxy0b3xQR .image-shape .label,#mermaid-svg-BK1OZsSgxy0b3xQR .icon-shape .label{text-align:center;}#mermaid-svg-BK1OZsSgxy0b3xQR .node.clickable{cursor:pointer;}#mermaid-svg-BK1OZsSgxy0b3xQR .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-BK1OZsSgxy0b3xQR .arrowheadPath{fill:#333333;}#mermaid-svg-BK1OZsSgxy0b3xQR .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-BK1OZsSgxy0b3xQR .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-BK1OZsSgxy0b3xQR .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BK1OZsSgxy0b3xQR .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-BK1OZsSgxy0b3xQR .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BK1OZsSgxy0b3xQR .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-BK1OZsSgxy0b3xQR .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-BK1OZsSgxy0b3xQR .cluster text{fill:#333;}#mermaid-svg-BK1OZsSgxy0b3xQR .cluster span{color:#333;}#mermaid-svg-BK1OZsSgxy0b3xQR div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-BK1OZsSgxy0b3xQR .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-BK1OZsSgxy0b3xQR rect.text{fill:none;stroke-width:0;}#mermaid-svg-BK1OZsSgxy0b3xQR .icon-shape,#mermaid-svg-BK1OZsSgxy0b3xQR .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BK1OZsSgxy0b3xQR .icon-shape p,#mermaid-svg-BK1OZsSgxy0b3xQR .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-BK1OZsSgxy0b3xQR .icon-shape .label rect,#mermaid-svg-BK1OZsSgxy0b3xQR .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BK1OZsSgxy0b3xQR .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-BK1OZsSgxy0b3xQR .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-BK1OZsSgxy0b3xQR :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} shallow(新对象)
original
name: 'Alice'
address →
name: 'Alice'(独立副本)
address →
内存中的 address 对象

city: '北京'

9.3.4 深拷贝(Deep Copy)

深拷贝递归地复制所有层级的对象,确保新对象与原对象完全独立。

javascript 复制代码
// 方式1:JSON 序列化(简单但有局限)
const original = {
  name: 'Alice',
  address: { city: '北京', tags: ['VIP', '活跃'] },
  score: 95
};

const deep = JSON.parse(JSON.stringify(original));
deep.address.city = '上海';
deep.address.tags.push('新标签');

console.log(original.address.city);  // "北京"(未受影响 ✅)
console.log(original.address.tags);  // ['VIP', '活跃'](未受影响 ✅)

JSON 深拷贝的局限性

javascript 复制代码
const obj = {
  date: new Date(),
  regex: /test/gi,
  fn: () => console.log('hello'),
  undef: undefined,
  map: new Map(),
  circular: null
};
obj.circular = obj; // 循环引用

// ❌ JSON.stringify 无法处理:
// - Date → 变成字符串
// - RegExp → 变成空对象 {}
// - Function → 被忽略
// - undefined → 被忽略
// - Map/Set → 变成空对象 {}
// - 循环引用 → 抛出 TypeError

const bad = JSON.parse(JSON.stringify(obj));
console.log(bad.date instanceof Date);  // false(变成了字符串)
console.log(bad.fn);                    // undefined(函数被忽略了)

总结JSON.parse(JSON.stringify()) 适合纯数据对象(只包含 number、string、boolean、Array、Object 且无循环引用)。对于包含特殊类型或循环引用的对象,需要手写深拷贝(见实战部分)。

9.3.5 structuredClone(现代方案)

ES2022 引入了 structuredClone,它是浏览器和 Node.js 内置的深拷贝函数,能处理大多数特殊类型和循环引用。

javascript 复制代码
const original = {
  name: 'Alice',
  date: new Date(),
  regex: /test/gi,
  map: new Map([['key', 'value']]),
  set: new Set([1, 2, 3]),
  nested: { arr: [1, 2, { deep: true }] }
};

// structuredClone 能正确处理
const cloned = structuredClone(original);

console.log(cloned.date instanceof Date);     // true ✅
console.log(cloned.regex instanceof RegExp);  // true ✅
console.log(cloned.map instanceof Map);       // true ✅
console.log(cloned.nested === original.nested); // false ✅(独立副本)

// 甚至能处理循环引用
const circular = { name: 'test' };
circular.self = circular;
const clonedCircular = structuredClone(circular);
console.log(clonedCircular.self === clonedCircular); // true ✅

structuredClone 的局限

能力 支持情况
普通对象、数组
Date、RegExp、Map、Set
ArrayBuffer、TypedArray
循环引用
Function ❌ 抛出 DataCloneError
DOM 节点 ❌ 抛出 DataCloneError
Symbol 键 ❌ 不会被复制
深拷贝方案 特殊类型 循环引用 函数 推荐场景
JSON.parse(JSON.stringify()) 纯数据对象(API 数据)
structuredClone() 现代环境中的通用方案
手写深拷贝 需要完全控制的场景

9.4 垃圾回收机制基础

JavaScript 有自动的垃圾回收(Garbage Collection)机制,开发者不需要手动释放内存。理解垃圾回收的基本原理有助于避免内存泄漏。

9.4.1 可达性(Reachability)

垃圾回收的核心概念是可达性:如果一个值从任何根(Root)出发都无法被引用到,它就是"不可达"的,会被回收。

**根(Roots)**包括:

  • 全局变量(window 对象的属性)
  • 当前函数的局部变量和参数
  • 调用栈中其他函数的变量和参数
javascript 复制代码
// 可达示例
let user = { name: 'Alice' }; // user 变量是根,对象可达

// 让对象变为不可达
user = null; // 原来的 { name: 'Alice' } 不再被引用,会被回收

#mermaid-svg-jtIB6jMpF07j31a6{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-jtIB6jMpF07j31a6 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-jtIB6jMpF07j31a6 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-jtIB6jMpF07j31a6 .error-icon{fill:#552222;}#mermaid-svg-jtIB6jMpF07j31a6 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-jtIB6jMpF07j31a6 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-jtIB6jMpF07j31a6 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-jtIB6jMpF07j31a6 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-jtIB6jMpF07j31a6 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-jtIB6jMpF07j31a6 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-jtIB6jMpF07j31a6 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-jtIB6jMpF07j31a6 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-jtIB6jMpF07j31a6 .marker.cross{stroke:#333333;}#mermaid-svg-jtIB6jMpF07j31a6 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-jtIB6jMpF07j31a6 p{margin:0;}#mermaid-svg-jtIB6jMpF07j31a6 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-jtIB6jMpF07j31a6 .cluster-label text{fill:#333;}#mermaid-svg-jtIB6jMpF07j31a6 .cluster-label span{color:#333;}#mermaid-svg-jtIB6jMpF07j31a6 .cluster-label span p{background-color:transparent;}#mermaid-svg-jtIB6jMpF07j31a6 .label text,#mermaid-svg-jtIB6jMpF07j31a6 span{fill:#333;color:#333;}#mermaid-svg-jtIB6jMpF07j31a6 .node rect,#mermaid-svg-jtIB6jMpF07j31a6 .node circle,#mermaid-svg-jtIB6jMpF07j31a6 .node ellipse,#mermaid-svg-jtIB6jMpF07j31a6 .node polygon,#mermaid-svg-jtIB6jMpF07j31a6 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-jtIB6jMpF07j31a6 .rough-node .label text,#mermaid-svg-jtIB6jMpF07j31a6 .node .label text,#mermaid-svg-jtIB6jMpF07j31a6 .image-shape .label,#mermaid-svg-jtIB6jMpF07j31a6 .icon-shape .label{text-anchor:middle;}#mermaid-svg-jtIB6jMpF07j31a6 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-jtIB6jMpF07j31a6 .rough-node .label,#mermaid-svg-jtIB6jMpF07j31a6 .node .label,#mermaid-svg-jtIB6jMpF07j31a6 .image-shape .label,#mermaid-svg-jtIB6jMpF07j31a6 .icon-shape .label{text-align:center;}#mermaid-svg-jtIB6jMpF07j31a6 .node.clickable{cursor:pointer;}#mermaid-svg-jtIB6jMpF07j31a6 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-jtIB6jMpF07j31a6 .arrowheadPath{fill:#333333;}#mermaid-svg-jtIB6jMpF07j31a6 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-jtIB6jMpF07j31a6 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-jtIB6jMpF07j31a6 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jtIB6jMpF07j31a6 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-jtIB6jMpF07j31a6 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jtIB6jMpF07j31a6 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-jtIB6jMpF07j31a6 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-jtIB6jMpF07j31a6 .cluster text{fill:#333;}#mermaid-svg-jtIB6jMpF07j31a6 .cluster span{color:#333;}#mermaid-svg-jtIB6jMpF07j31a6 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-jtIB6jMpF07j31a6 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-jtIB6jMpF07j31a6 rect.text{fill:none;stroke-width:0;}#mermaid-svg-jtIB6jMpF07j31a6 .icon-shape,#mermaid-svg-jtIB6jMpF07j31a6 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jtIB6jMpF07j31a6 .icon-shape p,#mermaid-svg-jtIB6jMpF07j31a6 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-jtIB6jMpF07j31a6 .icon-shape .label rect,#mermaid-svg-jtIB6jMpF07j31a6 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jtIB6jMpF07j31a6 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-jtIB6jMpF07j31a6 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-jtIB6jMpF07j31a6 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 根(全局变量/局部变量)
user 变量
对象 { name: 'Alice' }
嵌套对象 { city: '北京' }
其他变量
其他对象

9.4.2 垃圾回收算法:标记-清除

现代 JavaScript 引擎主要使用标记-清除(Mark-and-Sweep)算法:

  1. 标记阶段:从根出发,递归标记所有可达的对象
  2. 清除阶段:遍历堆内存,删除所有未被标记的对象
  3. 整理阶段:将存活对象紧凑排列,减少内存碎片

#mermaid-svg-UfJau9gxwNFKYcXK{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-UfJau9gxwNFKYcXK .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-UfJau9gxwNFKYcXK .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-UfJau9gxwNFKYcXK .error-icon{fill:#552222;}#mermaid-svg-UfJau9gxwNFKYcXK .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-UfJau9gxwNFKYcXK .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-UfJau9gxwNFKYcXK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-UfJau9gxwNFKYcXK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-UfJau9gxwNFKYcXK .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-UfJau9gxwNFKYcXK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-UfJau9gxwNFKYcXK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-UfJau9gxwNFKYcXK .marker{fill:#333333;stroke:#333333;}#mermaid-svg-UfJau9gxwNFKYcXK .marker.cross{stroke:#333333;}#mermaid-svg-UfJau9gxwNFKYcXK svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-UfJau9gxwNFKYcXK p{margin:0;}#mermaid-svg-UfJau9gxwNFKYcXK .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-UfJau9gxwNFKYcXK .cluster-label text{fill:#333;}#mermaid-svg-UfJau9gxwNFKYcXK .cluster-label span{color:#333;}#mermaid-svg-UfJau9gxwNFKYcXK .cluster-label span p{background-color:transparent;}#mermaid-svg-UfJau9gxwNFKYcXK .label text,#mermaid-svg-UfJau9gxwNFKYcXK span{fill:#333;color:#333;}#mermaid-svg-UfJau9gxwNFKYcXK .node rect,#mermaid-svg-UfJau9gxwNFKYcXK .node circle,#mermaid-svg-UfJau9gxwNFKYcXK .node ellipse,#mermaid-svg-UfJau9gxwNFKYcXK .node polygon,#mermaid-svg-UfJau9gxwNFKYcXK .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-UfJau9gxwNFKYcXK .rough-node .label text,#mermaid-svg-UfJau9gxwNFKYcXK .node .label text,#mermaid-svg-UfJau9gxwNFKYcXK .image-shape .label,#mermaid-svg-UfJau9gxwNFKYcXK .icon-shape .label{text-anchor:middle;}#mermaid-svg-UfJau9gxwNFKYcXK .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-UfJau9gxwNFKYcXK .rough-node .label,#mermaid-svg-UfJau9gxwNFKYcXK .node .label,#mermaid-svg-UfJau9gxwNFKYcXK .image-shape .label,#mermaid-svg-UfJau9gxwNFKYcXK .icon-shape .label{text-align:center;}#mermaid-svg-UfJau9gxwNFKYcXK .node.clickable{cursor:pointer;}#mermaid-svg-UfJau9gxwNFKYcXK .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-UfJau9gxwNFKYcXK .arrowheadPath{fill:#333333;}#mermaid-svg-UfJau9gxwNFKYcXK .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-UfJau9gxwNFKYcXK .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-UfJau9gxwNFKYcXK .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-UfJau9gxwNFKYcXK .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-UfJau9gxwNFKYcXK .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-UfJau9gxwNFKYcXK .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-UfJau9gxwNFKYcXK .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-UfJau9gxwNFKYcXK .cluster text{fill:#333;}#mermaid-svg-UfJau9gxwNFKYcXK .cluster span{color:#333;}#mermaid-svg-UfJau9gxwNFKYcXK div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-UfJau9gxwNFKYcXK .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-UfJau9gxwNFKYcXK rect.text{fill:none;stroke-width:0;}#mermaid-svg-UfJau9gxwNFKYcXK .icon-shape,#mermaid-svg-UfJau9gxwNFKYcXK .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-UfJau9gxwNFKYcXK .icon-shape p,#mermaid-svg-UfJau9gxwNFKYcXK .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-UfJau9gxwNFKYcXK .icon-shape .label rect,#mermaid-svg-UfJau9gxwNFKYcXK .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-UfJau9gxwNFKYcXK .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-UfJau9gxwNFKYcXK .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-UfJau9gxwNFKYcXK :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 1. 标记阶段
2. 清除阶段
3. 整理阶段
从根出发,标记所有可达对象
删除未标记的对象
紧凑排列存活对象

9.4.3 常见内存泄漏场景

虽然 JavaScript 有自动垃圾回收,但不当的代码仍然会导致内存泄漏------对象不再使用但依然被引用,无法被回收。

javascript 复制代码
// 1. 意外的全局变量
function leak() {
  // 忘记声明 let/const,在非严格模式下创建全局变量
  leakedData = []; // 相当于 window.leakedData = []
}

// 2. 未清理的定时器
const element = document.getElementById('myDiv');
setInterval(() => {
  element.textContent = Date.now(); // element 被闭包引用,无法被回收
}, 1000);
// 如果 element 从 DOM 中移除,定时器仍在运行 → 内存泄漏

// 3. 未清理的事件监听
const button = document.getElementById('btn');
button.addEventListener('click', function handler() {
  // handler 引用了 button 所在的作用域中的大型数据
});
// button 从 DOM 移除后,如果没有 removeEventListener,可能导致泄漏

// 4. 闭包持有大对象引用
function processData() {
  const hugeData = new Array(1000000); // 大数据
  return function() {
    console.log(hugeData.length); // 闭包持有 hugeData 的引用
  };
}
const processor = processData();
// 即使 hugeData 不再需要,闭包依然持有它的引用

最佳实践 :不再使用的大对象及时设为 null,定时器和事件监听在组件销毁时清理,避免闭包无意中持有大数据的引用。


9.5 Object 静态方法

Object 构造函数提供了许多实用的静态方法,用于操作和查询对象。

9.5.1 Object.keys / values / entries

这三个方法是遍历对象最常用的工具。

javascript 复制代码
const user = {
  name: 'Alice',
  age: 25,
  city: '北京'
};

// Object.keys → 属性名数组
console.log(Object.keys(user));
// ['name', 'age', 'city']

// Object.values → 属性值数组
console.log(Object.values(user));
// ['Alice', 25, '北京']

// Object.entries → 键值对数组
console.log(Object.entries(user));
// [['name', 'Alice'], ['age', 25], ['city', '北京']]

实际应用

javascript 复制代码
const prices = { apple: 10, banana: 6, orange: 8 };

// 过滤价格大于 7 的水果
const expensive = Object.entries(prices)
  .filter(([fruit, price]) => price > 7)
  .map(([fruit]) => fruit);
console.log(expensive); // ['apple', 'orange']

// 将对象转换为排序后的新对象
const sorted = Object.fromEntries(
  Object.entries(prices).sort((a, b) => a[1] - b[1])
);
console.log(sorted); // { banana: 6, orange: 8, apple: 10 }

// 统计对象属性个数
const count = Object.keys(user).length;
console.log(count); // 3

9.5.2 Object.assign:对象合并

Object.assign(target, ...sources) 将一个或多个源对象的可枚举属性复制到目标对象,并返回目标对象。

javascript 复制代码
const target = { a: 1, b: 2 };
const source1 = { b: 3, c: 4 };
const source2 = { c: 5, d: 6 };

const result = Object.assign(target, source1, source2);

console.log(target);  // { a: 1, b: 3, c: 5, d: 6 }
console.log(result);  // 与 target 是同一个对象(=== 为 true)

注意Object.assign修改目标对象 。如果不想修改原对象,传入空对象 {} 作为目标。

javascript 复制代码
const original = { a: 1, b: 2 };

// ❌ 修改了 original
Object.assign(original, { c: 3 });
console.log(original); // { a: 1, b: 2, c: 3 }

// ✅ 不修改 original,创建新对象
const merged = Object.assign({}, original, { d: 4 });
console.log(original); // { a: 1, b: 2, c: 3 }(不含 d)
console.log(merged);   // { a: 1, b: 2, c: 3, d: 4 }

Object.assign 是浅拷贝

javascript 复制代码
const source = { nested: { value: 1 } };
const target = Object.assign({}, source);

target.nested.value = 999;
console.log(source.nested.value); // 999(嵌套对象是共享引用)

9.5.3 Object.freeze / seal / preventExtensions

这三个方法用于限制对象的修改能力,提供不同程度的"冻结"。

javascript 复制代码
// Object.freeze:完全冻结(不能添加、删除、修改属性)
const frozen = Object.freeze({ name: 'Alice', age: 25 });
frozen.name = 'Bob';       // 静默失败(严格模式下报错)
frozen.email = 'a@b.com';  // 静默失败
delete frozen.age;          // 静默失败
console.log(frozen);        // { name: 'Alice', age: 25 }(完全不变)

// Object.seal:密封(不能添加、删除属性,但可以修改已有属性)
const sealed = Object.seal({ name: 'Alice', age: 25 });
sealed.name = 'Bob';       // ✅ 可以修改
sealed.email = 'a@b.com';  // 静默失败
delete sealed.age;          // 静默失败
console.log(sealed);        // { name: 'Bob', age: 25 }

// Object.preventExtensions:阻止添加新属性(但可以修改和删除已有属性)
const ext = Object.preventExtensions({ name: 'Alice', age: 25 });
ext.name = 'Bob';          // ✅ 可以修改
ext.email = 'a@b.com';     // 静默失败
delete ext.age;             // ✅ 可以删除
console.log(ext);           // { name: 'Bob' }
方法 添加属性 删除属性 修改属性 典型用途
Object.freeze 常量配置、不可变数据
Object.seal 固定结构的对象
Object.preventExtensions 防止意外添加属性

注意 :这三个方法都是浅冻结------只影响对象的第一层属性,嵌套对象不受影响。

javascript 复制代码
const config = Object.freeze({
  theme: 'dark',
  database: {
    host: 'localhost',
    port: 3306
  }
});

config.theme = 'light';           // 静默失败 ✅(第一层被冻结)
config.database.host = 'remote';  // ✅ 成功!(嵌套对象未被冻结)
console.log(config.database.host); // "remote"

9.5.4 其他常用 Object 方法

javascript 复制代码
const obj = { a: 1, b: 2, c: 3 };

// Object.hasOwn:检查自身属性(替代 hasOwnProperty)
console.log(Object.hasOwn(obj, 'a'));       // true
console.log(Object.hasOwn(obj, 'toString')); // false

// Object.fromEntries:从键值对数组创建对象(entries 的逆操作)
const entries = [['name', 'Alice'], ['age', 25]];
const user = Object.fromEntries(entries);
console.log(user); // { name: 'Alice', age: 25 }

// Object.is:精确相等比较(比 === 更精确)
console.log(Object.is(NaN, NaN));    // true(=== 返回 false)
console.log(Object.is(0, -0));       // false(=== 返回 true)
console.log(Object.is(1, 1));        // true
console.log(Object.is(null, null));  // true

// Object.getPrototypeOf:获取对象的原型
console.log(Object.getPrototypeOf([]) === Array.prototype); // true

9.6 实战:对象合并与深拷贝实现

9.6.1 实战1:智能对象合并

实现一个 deepMerge 函数,递归合并两个对象,而不是简单覆盖。

javascript 复制代码
/**
 * 深度合并两个对象
 * - 相同属性:源对象覆盖目标对象
 * - 嵌套对象:递归合并(而非覆盖)
 * - 数组:源对象覆盖目标对象
 * @param {Object} target - 目标对象
 * @param {Object} source - 源对象
 * @returns {Object} 合并后的新对象
 */
function deepMerge(target, source) {
  const result = { ...target };

  for (const key of Object.keys(source)) {
    const targetVal = target[key];
    const sourceVal = source[key];

    // 如果两边都是纯对象(非数组),递归合并
    if (isPlainObject(targetVal) && isPlainObject(sourceVal)) {
      result[key] = deepMerge(targetVal, sourceVal);
    } else {
      result[key] = sourceVal;
    }
  }

  return result;
}

/**
 * 判断是否为纯对象(排除数组、Date、RegExp 等)
 */
function isPlainObject(value) {
  return (
    value !== null &&
    typeof value === 'object' &&
    !Array.isArray(value) &&
    Object.getPrototypeOf(value) === Object.prototype
  );
}

// 测试
const defaultConfig = {
  server: {
    host: 'localhost',
    port: 3000,
    ssl: false
  },
  database: {
    host: 'localhost',
    port: 3306,
    name: 'mydb'
  },
  debug: false
};

const userConfig = {
  server: {
    port: 8080,
    ssl: true
  },
  database: {
    host: '192.168.1.100'
  },
  debug: true
};

const config = deepMerge(defaultConfig, userConfig);

console.log(config);
// {
//   server: { host: 'localhost', port: 8080, ssl: true },  ← 递归合并
//   database: { host: '192.168.1.100', port: 3306, name: 'mydb' },  ← 递归合并
//   debug: true
// }

9.6.2 实战2:手写深拷贝

实现一个支持循环引用的深拷贝函数。

javascript 复制代码
/**
 * 手写深拷贝
 * 支持:普通对象、数组、Date、RegExp、Map、Set、循环引用
 * @param {*} obj - 要拷贝的值
 * @param {WeakMap} cache - 缓存(处理循环引用)
 * @returns {*} 深拷贝后的值
 */
function deepClone(obj, cache = new WeakMap()) {
  // 1. 原始类型和函数直接返回
  if (obj === null || typeof obj !== 'object') {
    return obj;
  }

  // 2. 处理循环引用
  if (cache.has(obj)) {
    return cache.get(obj);
  }

  // 3. 处理特殊类型
  if (obj instanceof Date) {
    return new Date(obj.getTime());
  }

  if (obj instanceof RegExp) {
    return new RegExp(obj.source, obj.flags);
  }

  // 4. 创建新对象/数组
  const clone = Array.isArray(obj) ? [] : {};

  // 5. 记录到缓存(必须在递归之前,防止循环引用导致无限递归)
  cache.set(obj, clone);

  // 6. 处理 Map
  if (obj instanceof Map) {
    obj.forEach((value, key) => {
      clone.set(deepClone(key, cache), deepClone(value, cache));
    });
    return clone;
  }

  // 7. 处理 Set
  if (obj instanceof Set) {
    obj.forEach(value => {
      clone.add(deepClone(value, cache));
    });
    return clone;
  }

  // 8. 递归复制普通对象/数组的属性
  for (const key of Object.keys(obj)) {
    clone[key] = deepClone(obj[key], cache);
  }

  return clone;
}

// ===== 测试 =====

// 测试1:嵌套对象
const original = {
  name: 'Alice',
  address: { city: '北京', tags: ['VIP'] },
  scores: [90, 85, 92]
};

const cloned = deepClone(original);
cloned.address.city = '上海';
cloned.address.tags.push('新标签');
cloned.scores[0] = 100;

console.log(original.address.city);  // "北京" ✅(独立副本)
console.log(original.address.tags);  // ['VIP'] ✅(独立副本)
console.log(original.scores);        // [90, 85, 92] ✅(独立副本)

// 测试2:循环引用
const circular = { name: 'test' };
circular.self = circular;

const clonedCircular = deepClone(circular);
console.log(clonedCircular.name);              // "test" ✅
console.log(clonedCircular.self === clonedCircular); // true ✅
console.log(clonedCircular.self === circular);       // false ✅(不是同一个对象)

// 测试3:特殊类型
const special = {
  date: new Date('2024-01-01'),
  regex: /hello/gi,
  map: new Map([['key1', { nested: true }]]),
  set: new Set([1, 2, 3])
};

const clonedSpecial = deepClone(special);
console.log(clonedSpecial.date instanceof Date);    // true ✅
console.log(clonedSpecial.date === special.date);    // false ✅(独立副本)
console.log(clonedSpecial.regex instanceof RegExp);  // true ✅
console.log(clonedSpecial.map instanceof Map);       // true ✅

9.6.3 深拷贝实现要点解析

#mermaid-svg-kUPdcDxBR7G19Rrb{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-kUPdcDxBR7G19Rrb .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-kUPdcDxBR7G19Rrb .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-kUPdcDxBR7G19Rrb .error-icon{fill:#552222;}#mermaid-svg-kUPdcDxBR7G19Rrb .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-kUPdcDxBR7G19Rrb .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-kUPdcDxBR7G19Rrb .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-kUPdcDxBR7G19Rrb .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-kUPdcDxBR7G19Rrb .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-kUPdcDxBR7G19Rrb .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-kUPdcDxBR7G19Rrb .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-kUPdcDxBR7G19Rrb .marker{fill:#333333;stroke:#333333;}#mermaid-svg-kUPdcDxBR7G19Rrb .marker.cross{stroke:#333333;}#mermaid-svg-kUPdcDxBR7G19Rrb svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-kUPdcDxBR7G19Rrb p{margin:0;}#mermaid-svg-kUPdcDxBR7G19Rrb .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-kUPdcDxBR7G19Rrb .cluster-label text{fill:#333;}#mermaid-svg-kUPdcDxBR7G19Rrb .cluster-label span{color:#333;}#mermaid-svg-kUPdcDxBR7G19Rrb .cluster-label span p{background-color:transparent;}#mermaid-svg-kUPdcDxBR7G19Rrb .label text,#mermaid-svg-kUPdcDxBR7G19Rrb span{fill:#333;color:#333;}#mermaid-svg-kUPdcDxBR7G19Rrb .node rect,#mermaid-svg-kUPdcDxBR7G19Rrb .node circle,#mermaid-svg-kUPdcDxBR7G19Rrb .node ellipse,#mermaid-svg-kUPdcDxBR7G19Rrb .node polygon,#mermaid-svg-kUPdcDxBR7G19Rrb .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-kUPdcDxBR7G19Rrb .rough-node .label text,#mermaid-svg-kUPdcDxBR7G19Rrb .node .label text,#mermaid-svg-kUPdcDxBR7G19Rrb .image-shape .label,#mermaid-svg-kUPdcDxBR7G19Rrb .icon-shape .label{text-anchor:middle;}#mermaid-svg-kUPdcDxBR7G19Rrb .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-kUPdcDxBR7G19Rrb .rough-node .label,#mermaid-svg-kUPdcDxBR7G19Rrb .node .label,#mermaid-svg-kUPdcDxBR7G19Rrb .image-shape .label,#mermaid-svg-kUPdcDxBR7G19Rrb .icon-shape .label{text-align:center;}#mermaid-svg-kUPdcDxBR7G19Rrb .node.clickable{cursor:pointer;}#mermaid-svg-kUPdcDxBR7G19Rrb .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-kUPdcDxBR7G19Rrb .arrowheadPath{fill:#333333;}#mermaid-svg-kUPdcDxBR7G19Rrb .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-kUPdcDxBR7G19Rrb .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-kUPdcDxBR7G19Rrb .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-kUPdcDxBR7G19Rrb .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-kUPdcDxBR7G19Rrb .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-kUPdcDxBR7G19Rrb .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-kUPdcDxBR7G19Rrb .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-kUPdcDxBR7G19Rrb .cluster text{fill:#333;}#mermaid-svg-kUPdcDxBR7G19Rrb .cluster span{color:#333;}#mermaid-svg-kUPdcDxBR7G19Rrb div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-kUPdcDxBR7G19Rrb .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-kUPdcDxBR7G19Rrb rect.text{fill:none;stroke-width:0;}#mermaid-svg-kUPdcDxBR7G19Rrb .icon-shape,#mermaid-svg-kUPdcDxBR7G19Rrb .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-kUPdcDxBR7G19Rrb .icon-shape p,#mermaid-svg-kUPdcDxBR7G19Rrb .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-kUPdcDxBR7G19Rrb .icon-shape .label rect,#mermaid-svg-kUPdcDxBR7G19Rrb .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-kUPdcDxBR7G19Rrb .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-kUPdcDxBR7G19Rrb .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-kUPdcDxBR7G19Rrb :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是



Date
RegExp
Map/Set
普通对象/数组
deepClone(obj)
typeof !== 'object'

或 null?
直接返回原始值
cache 中存在?
返回缓存的副本

(处理循环引用)
特殊类型?
new Date(time)
new RegExp(source, flags)
创建新 Map/Set

递归复制元素
创建新 {}/\[\]
cache.set(obj, clone)
递归复制每个属性

为什么用 WeakMap 做缓存? WeakMap 的键是弱引用,不会阻止垃圾回收。如果用普通 Map,被拷贝的对象在函数结束后仍然被 Map 引用,无法被垃圾回收,造成内存泄漏。


本章小结

知识点 要点
对象字面量 {} 创建对象,推荐方式,支持属性简写和计算属性名
new Object() 构造函数方式,冗长不推荐
点号 . 访问已知属性,语法简洁
方括号 [] 访问动态键或含特殊字符的键
delete 删除对象属性,成功返回 true
in 操作符 检查属性是否存在(含原型链)
Object.hasOwn 只检查自身属性(现代推荐写法)
引用赋值 对象赋值复制的是引用,不是值
浅拷贝 { ...obj }Object.assign,只复制第一层
深拷贝 structuredClone 或手写递归,所有层级独立
垃圾回收 标记-清除算法,不可达的对象会被自动回收
Object.keys/values/entries 获取对象的属性名、属性值、键值对数组
Object.assign 合并对象(浅拷贝),修改目标对象
Object.freeze 冻结对象,不能增删改属性(浅冻结)

一句话总结 :对象是 JavaScript 的"万能容器"。理解引用 的本质是避免 Bug 的关键------赋值只是复制地址,浅拷贝只复制第一层,深拷贝才能真正独立。Object.keys/values/entries 三件套是遍历对象的标准工具,structuredClone 是现代环境中最方便的深拷贝方案。


下篇预告第10章:对象方法与 this --- 我们将学习如何在对象中定义方法,深入理解 this 的四种绑定规则,以及 call / apply / bind 的原理和使用。


作者 :xhmico | CSDN 博客https://blog.csdn.net/xhmico

参考资源The Modern JavaScript Tutorial | MDN Web Docs | ECMAScript 规范

相关推荐
2601_949818091 小时前
Vector从入门到应用(C++ STL动态数组万字全解
开发语言·c++
后台开发者Ethan2 小时前
setState组件更新
前端·javascript·react
汤米粥2 小时前
Python爬虫中Xpath用法之——normalize-space()
开发语言·python
科技道人2 小时前
记录 设置-显示大小和字体
开发语言·android设置·字体和显示大小
浮江雾2 小时前
Flutter第三节----Dart中的数据类型
android·开发语言·学习·flutter·入门·函数
weixin_422329312 小时前
AgentScope Java 项目入门 & Builder 深度解读
java·开发语言·人工智能
chh5632 小时前
C++--list
开发语言·数据结构·c++·学习·算法·list
蜡台2 小时前
uniapp vue2 转 vue3
前端·javascript·uni-app·vue3·vue2
WindfallSheng3 小时前
从Vibe Coding到Spec Coding:一套可落地的AI-SDD企业级研发实战工程
javascript·vue.js