首页/文章/八股文

对象深拷贝的原理和代码实现

2025-01-13
9614 分钟
...

关键词:对象什拷贝原理、避免循环引用和栈溢出

在 JavaScript 中,对象深拷贝指的是创建一个对象的副本,使得这个副本与原始对象不共享任何一个引用。这意味着,如果你修改了副本的属性,原始对象不会受到任何影响,反之亦然。

原理

在实现深拷贝时,有几个关键的概念需要理解:

  1. 值类型与引用类型:值类型(如数字、字符串和布尔值)直接存储数据的值,而引用类型(如对象、数组等)存储的是对一个内存地址的引用。

  2. 复制引用:如果你将一个对象赋值给一个新的变量,那么这个变量仅复制了对象的引用,而不是对象本身。因此,两个变量都指向同一个对象。

  3. 深度克隆:深拷贝则需要递归地复制对象中的每个属性,确保每个属性都是独立的副本,而不共享引用。

实现

实现对象的深拷贝有多种方式,以下是几种常见的实现方法:

1. JSON 方法

最简单的深拷贝方法之一是使用 JSON.stringify()JSON.parse()

function deepClone(value) {
  return JSON.parse(JSON.stringify(value));
}

但是,这种方法有局限性:

  • 它无法复制函数。
  • 它无法复制循环引用。
  • 它不会拷贝 undefined
  • 它无法处理特定属性(如 Symbol 属性、属性名为 Symbol 类型的属性等)。

2. 递归方法

你可以编写一个递归函数来复制每个属性:

function deepClone(value) {
  if (typeof value !== "object" || value === null) {
    return value; // 返回原始值类型
  }

  let result = Array.isArray(value) ? [] : {};
  for (let key in value) {
    // 使用 hasOwnProperty 检查以避免原型链中的键
    if (value.hasOwnProperty(key)) {
      // 递归复制每个属性值
      result[key] = deepClone(value[key]);
    }
  }
  return result;
}

这种方法的优点是它可以处理循环引用的拷贝,并且能够处理函数以外的所有类型的值。但它仍然有局限性,比如它不会拷贝对象的原型链。

补充进阶:避免循环引用和栈溢出的问题且支持拷贝原型链上的属性

为了避免循环引用和栈溢出的问题,我们可以在递归函数中加入一个缓存(通常是对象或 Map),来存储已经被拷贝过的引用类型对象。这样,当遇到一个已经被拷贝的引用类型时,我们可以使用缓存中的数据而不是再次进行拷贝。

下面是实现该思想的深拷贝函数示例:

function deepClone(value, map = new WeakMap()) {
  if (typeof value !== "object" || value === null) {
    return value; // 返回基本数据类型的值
  }

  // 检查是否为 Date、RegExp、Function 或循环引用
  if (value instanceof Date || value instanceof RegExp) {
    return value; // Created with built-in constructors, directly returned
  }

  // 如果 map 中已存在,则返回之前拷贝的对象,避免循环引用
  if (map.has(value)) {
    return map.get(value);
  }

  let result;
  if (Array.isArray(value)) {
    result = [];
    map.set(value, result);
    for (let i = 0; i < value.length; i++) {
      result[i] = deepClone(value[i], map); // 处理数组循环引用
    }
  } else {
    result = {};
    map.set(value, result);
    for (let key in value) {
      if (value.hasOwnProperty(key)) {
        result[key] = deepClone(value[key], map); // 递归复制每个属性
      }
    }
  }

  // 拷贝原型链上的属性
  // 根据需要可以取消以下注释
  // result.__proto__ = Object.getPrototypeOf(value);

  return result;
}

3. 使用第三方库

另一个选择是使用第三方库,如 Lodash,它提供了 _.cloneDeep 方法来实现深拷贝:

const _ = require("lodash");
const clone = _.cloneDeep(yourObject);

使用第三方库通常是最简单且最健壮的解决方案,因为它们已经考虑到了各种边缘情况,并包含了更高级的拷贝功能。

注意

无论选择哪种方法,都需要注意的是,深拷贝可能无法复制具有特定属性的对象,如:

  • Function 对象
  • Map 和 Set 对象
  • React 组件
  • 日期对象
  • 正则表达式对象
  • 以及一些其他通过构造函数创建的对象,可能会丢失它们的框架或库特定的属性或方法。

在实现深拷贝时,需要根据实际情况调整和选择使用的方法。

如果您觉得这篇文章有帮助,请点个赞吧~

分享文章

相关文章

更多文章 →
八股文2026-08-27
定时器按顺序播放多个音频,切后台音频会乱
本身不会补触发,但 它会被系统级冻结 ——iOS Safari 后台完全停止计时,切回前台后 只补执行一次 (不是不补,而是"缺失的中间状态补不上")。间隔短的不会出大问题, 间隔长的会出现"音频流被压缩" ——切回来后本该播 5 分钟的间隙,实际只过了 3 分钟,结果整段对不上。 核心原则 :定时器只能用来"提醒一次", 真正决定"该不该播"的是绝对时间戳 。 setTimeout 在后台会发生什么(按平台) | 平台 | 后台行为...
面试
八股文2026-08-27
线上项目白屏的原因
白屏的本质是 渲染管线某一环断了 ——可能是资源、JS、接口、路由、样式、兼容性任一环节出问题。排查按"控制台 → 网络 → DOM → 环境"四步定位。 本质(前端类比) 把网页想成一栋楼: 白屏 ≠ 一定是同一种原因——这是面试想听的层次。 六大类原因(按出现频率) | 类别 | 典型表现 | 真实案例 | | : | : | : | | ① 资源加载失败 | DOM 是空的 | 入口 JS 404、CDN 挂了、CSS 阻塞 |...
面试
八股文2026-08-27
背景图就是 1MB 大图,怎么优化
1MB 大背景图优化分 三步 :压缩体积(10 30x)、按需加载(按设备/视口/网速)、渲染期优化(GPU 合成)。背景图跟 不同——它是 CSS,不走浏览器的原生懒加载机制,得手动处理。 背景图 vs 的关键区别(先讲清楚这个) 这是面试官想听的"针对性认知"——背景图不是普通图片,不能套通用方案。 三步优化(按优先级) 第 1 步:压缩体积(最重要,立竿见影) 1MB 的来源一般是这几种 ,对应解决方案: | 原始问题 | 体积来...
面试
八股文2026-08-27
项目里很多图片和视频,怎么优化
图片视频优化分 四层 :网络层(CDN/格式)、加载层(懒加载/预加载)、渲染层(解码/缓存)、业务层(按需/降级)。面试要把这四层都讲到位才算有体系。 四层优化模型 第 1 层:网络层(省钱、省时间) 核心目标:让资源体积小、让用户拿到资源快 | 手段 | 作用 | 关键点 | | : | : | : | | 图片格式 | WebP/AVIF 比 JPEG 小 25 50% | 兼容 fallback | | 视频格式 | H.265...
面试
八股文2026-08-20
Embedding 向量模型:从语义表示到相似度计算
前言 大模型「读懂」文字靠的是 token,但 token 之间只有离散的编号关系,模型并不知道「苹果」和「李子」在语义上很近。要让程序能「理解」两段文字的相似程度,必须先把文本映射成一个 高维向量 ,再用几何方法比较。这一步就是 Embedding。 本篇基于我本地 今天的真实代码,从语义表示讲到余弦相似度,并复盘几个踩过的真实坑。 一、为什么需要 Embedding 传统关键词检索是「字面匹配」: Embedding 做的是「语义匹...
面试
八股文2026-08-19
前端面试100题
前端面试 100 题 适用方向:中高级前端 / React / Vue / Next.js / Nuxt / TypeScript / 工程化 / 实时通信 / Electron / Node.js / AI 应用前端 使用方式:优先掌握“标准回答”,再练“面试官追问”,最后把“结合你的简历怎么答”组织成自己的项目故事。 说明 “结合你的简历怎么答”只使用你简历中已经出现的项目与技术事实。 “标准回答 / 追问”属于通用前端知识总结,用...
面试

评论

请登录后发表评论

去登录
加载评论中...

目录