疯狂的技术宅

以前出于工作目的,编写和翻译了大量的技术文章,以前端为主,删掉了过时的、毫无营养的内容,留下的都是精华。


  • 首页

  • 分类

  • 标签

  • 归档

  • 关于本站

  • 回到主站

  • 搜索

在 JavaScript 中深度克隆对象(及其工作原理)

时间: 2019-07-04 分类: 前端技术   字数: 1421 字 阅读: 3分钟
标签: #JavaScript# #深拷贝# #浅拷贝# #Lodash#
  • 本文译自:https://alligator.io/js/deep-cloning-javascript-objects/
  • 译者:疯狂的技术宅

在 JavaScript 中深度克隆对象(及其工作原理)

如果你打算用 JavaScript 进行编码,那么就需要了解对象的工作方式。对象是 JavaScript 最重要的元素之一,深入理解了它会使你在编码时得心应手。在克隆对象时,它并不像看起来那么简单。

当你不想改变原始对象时,就需要克隆对象。例如,如果你有一个接受对象并改变它的函数,可能不想改变其原始对象。


那么让我们在 JavaScript 中创建一个对象:

let testObject = {
  a: 1,
  b: 2,
  c: 3
};

在上面的代码片段中,我们初始化一个新对象并将其分配给变量 testObject。现在对于大多数初学者来说,他们会试着通过将 testObject 分配给新变量来创建这个对象的副本,以便在其代码中进行操作。很抱歉用这种方法行不通。

下面是一个代码片段,说明了为什么不起作用。

let testObject = {
  a: 1,
  b: 2,
  c: 3
};

// 为 testObject 创建一个副本
let testObjectCopy = testObject;

testObject.a = 9;
console.log(testObjectCopy.a);
// 这里 a = 9

如上面的代码片段所示,创建新变量 testObjectCopy 实际上并不创建 testObject 的副本。相反它只是引用 testObject。你对所谓的副本做的任何更改也将反映在原始对象中。

循环遍历对象并将每个属性复制到新对象也不起作用。

const copyObject = object => {
  // 这是存储原始对象属性的对象
  let copiedObj = {};
  
  for (let key in object) {
    // 这里将每个属性从原始对象复制到复制对象
    copiedObj[key] = object[key];
  }

  return copiedObj;
};

const testObject = {
  a: 5,
  b: 6,
  c: {
    d: 4
  }
};

copyObject(testObject);

上述方法存在以下几个问题:

  • 1. 将每个属性复制到新对象的循环只会复制对象上的可枚举属性。可枚举属性是将要出现在 for 循环和 Object.keys 中的属性。
  • 2. 复制的对象有一个新的 Object.prototype 方法,这不是复制对象时所需的方法。
  • 3. 如果对象具有作为对象的属性,则复制的对象实际上将会引用原始对象而不是创建副本。这意味着如果更改复制对象中的嵌套对象,原始对象也会更改。
  • 4. 不复制任何属性描述符。如果将 configurable 或 writable 设置为 false,则复制对象中的属性描述符将会默认为 true。

那么应该怎样正确的复制对象?

对于仅存储基本类型(如数字和字符串)的简单对象,上述浅层复制方法将起作用。但是如果对象具有对其他嵌套对象的引用,则不会复制实际对象。你只会复制对其的引用。

对于深层复制,最简单的选择是使用可靠的外部库,如Lodash 。

使用 Lodash 的 Clone 和 Clonedeep

Lodash 提供两种不同的功能,允许你进行浅拷贝和深拷贝,它们是 clone 和 clonedeep。 Lodash 的优点在于你可以单独导入它的每个函数,而无需将整个库放入你的项目中。这可以大大的减少依赖项的大小。

const clone = require('lodash/clone'); 
const cloneDeep = require('lodash/clonedeep');

// 你也可以这样做:
// const clone = require('lodash.clone');
// const cloneDeep = require('lodash.clonedeep');
// 取决于你自己的风格 :)

现在就用 clone 和 clonedeep 函数做一些尝试:

const clone = require('lodash/clone'); 
const cloneDeep = require('lodash/clonedeep');

const externalObject = {
  animal: 'Gator'
};

const originalObject = {
  a: 1,
  b: 'string',
  c: false,
  d: externalObject
};

const shallowClonedObject = clone(originalObject);

externalObject.animal = 'Crocodile';

console.log(originalObject);
console.log(shallowClonedObject);
// originalObject 和 shallowClonedObject 中的`animal`属性
// 是同时被改变的,因为它是一个浅的副本。

const deepClonedObject = clonedeep(originalObject);

externalObject.animal = 'Lizard';

console.log(originalObject);
console.log(deepClonedObject);
// 原始对象中的'animal'属性发生了变化,但对于
// deepClonedObject,它复制后仍然是'Crocodile'
// 对象是独立的而不是复制引用。

在上面的代码中,我们创建了一个名为 originalObject 的对象,它存储了 7 个属性,每个属性都有不同的值。属性 d 引用我们的 externalObject,它具有值为 Gator 的 animal 的属性。

当从 Lodash 执行 clone 函数时,它会创建一个对象的浅层副本,我们将其分配给 shallowClonedObject。在 externalObject 中为 animal 属性赋值一个新值将改变 originalObject 和 shallowClonedObject,因为浅拷贝只能将引用复制到 externalObject并它没有为自己创造一个全新的对象。

这就是 clonedeep 函数的用武之地。如果你对 deepClonedObject 执行相同的处理,那么 originalObject 的 d 属性是唯一要改变的属性。

🤓试一试,看看它如何帮助你编码!🚀

标签: #JavaScript# #深拷贝# #浅拷贝# #Lodash#

标题:在 JavaScript 中深度克隆对象(及其工作原理)

链接:https://fe-tech.viewnode.com/post/201907/04/

作者:疯狂的技术宅

声明: 本博客文章除特别声明外,均采用 CC BY-NC-ND 4.0 国际许可协议( 知识共享署名-非商业性使用-禁止演绎 4.0),转载请注明出处!

怎样切换不同版本的 Node
Node.js 中的ES模块现状
  • 文章目录
  • 站点概览
疯狂的技术宅

疯狂的技术宅

退休程序员,硬件发烧友,人工智能爱好者。写写代码喝喝茶,晒晒太阳带带娃。

457 日志
8 分类
583 标签
GitHub
友情链接
  • viewnode
  • mofish
标签云
  • Javascript 172
  • Node.Js 62
  • Vue 36
  • Typescript 28
  • 实战项目 28
  • 面试 21
  • React 20
  • Css 17
  • 面试题 16
  • 教程 13
  • Promise 12
  • Chrome 9
  • Debug 9
  • 调试 9
  • 资源 9
  • Deno 8
  • Dom 8
  • 杂谈 8
  • 正则表达式 8
  • 测试 8
  • 那么应该怎样正确的复制对象?
    • 使用 Lodash 的 Clone 和 Clonedeep
© 2018 - 2022 疯狂的技术宅 All Rights Reserved
Powered by - Hugo v0.99.0 / Theme by - NexT
Storage by 俺的服务器 / 冀ICP备2022010157号
0%