【问题标题】:How to deep copy a custom object in JavaScript?如何在 JavaScript 中深度复制自定义对象?
【发布时间】:2017-02-19 11:17:54
【问题描述】:

我已经在这里浏览了一段时间,但仍然没有找到适合我的答案。

有没有办法在 JS 中深拷贝非普通对象?

我尝试了jQuery.extend(true, {}, this),但它只克隆了其中的一部分,其余的仍然作为对另一个对象的引用。

【问题讨论】:

  • “但它只是克隆了其中的一部分,其余的仍然作为对另一个对象的引用。” 您可以在 Question 中包含对象,创建 stacksn-ps 来演示吗?
  • 总是有JSON.parse(JSON.stringify(...)) hack。
  • @AkshatMahajan如果是JSON格式,没关系,OP提示有功能。
  • @Robmeister2015 this jQuery.extend(true, {}, this) 是什么?
  • “非普通对象”是什么意思?你的意思是 DOM 元素和函数之类的东西吗?深度复制这些意味着什么?

标签: javascript object clone


【解决方案1】:

这里有 3 种不同的复制对象的方法。每种方法都有优缺点,因此请仔细阅读并选择最适合您情况的方法

Object.assign 方法

使用Object.assign,“用于将所有可枚举自身属性的值从一个或多个源对象复制到目标对象”。这会复制值和函数。在撰写本文时,浏览器支持良好但并不完美,但这是 IMO 三种方法中最好的。

const obj1 = {a:1, b:2};
const obj1Copy = Object.assign(obj1)

传播算子方法

或者,您可以使用spread operator 从一个对象传播到另一个对象。请记住,这将复制键的值,但如果您的键的值是内存地址(其他嵌套对象或数组),那么它只会是浅拷贝。

const obj1 = {a: () => {}, b:2}
const obj1Copy = { ...obj1 }

JSON 字符串化/解析技巧

如果对象没有任何循环引用或函数作为值,则可以使用 json stringify 技巧:

let myCopy = JSON.parse(JSON.stringify(myObject));

不需要库,并且适用于大多数对象。

【讨论】:

  • 此副本是否可以在myObject 使用?
  • @guest271314 不。但是为什么要在数据结构中添加函数呢?函数必须在不同的地方作为例程。这只是 OOP 失败))
  • @Deep 具有将函数设置为值的属性的对象的示例是 documentwindow
  • @guest271314 函数!= 值。克隆文档或窗口?绝对不寻常。好的。我理解例如window.onload 但这是观察者事件的回调。理论上我们可以在没有父对象的情况下实现函数,将目标对象传递给第一个参数,如 C89 风格(例如 onload(img, function(it){/*...*/}))。我只看到在结构中使用函数的好处——复制保护(例如谷歌地图)。怪罪。现在所有的开发者都使用数据结构的函数而不是常规函数。
  • @Deep 即var obj = {"0":function(){}},其中"0" 是属性,obj["0"] 的值是function(){}。或者这是对示例的错误解释? “克隆文档或窗口?绝对不寻常。”可用于某些项目,请参阅AnswerEdit, save, self-modifying HTML document; format generated HTML, JavaScript
【解决方案2】:

你可以使用 lodash 的 cloneDeep 函数 - https://lodash.com/docs/4.16.4#cloneDeep

示例(来自文档)

var objects = [{ 'a': 1 }, { 'b': 2 }];

var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false

【讨论】:

    【解决方案3】:

    考虑到性能的快速克隆对象的方法。

    JSON.parse(JSON.stringify({"foo":"bar"}))
    

    性能如何? >> [这可能是深度复制对象的最佳方式]。我强烈建议您在 Youtube 上观看来自 Google Chrome Developers 社区的视频,解释此方法的工作原理和性能基准。

    注意:如果您的对象没有 Dates、functions、undefined、Infinity、RegExps、Maps、Sets、Blobs、FileLists、ImageDatas、sparse Arrays、Typed Arrays,请使用 JSON.parse 方法或其他复杂类型。 来源Read this SO answer


    快速提示 - React.JS 可以使用此解决方案从 localStorage 加载初始状态树。

    【讨论】:

    • 请注意,JSON.stringify/parse 总是创建一个普通的对象,去掉原始对象中可能存在的任何自定义方法。要在 stringify/parse 之后将方法恢复到副本,请使用 Object.setPrototypeOf(copy, Object.getPrototypeOf(original))
    【解决方案4】:

    如果你正在处理一个类实例,你可以使用这样的东西。

    您无需复制委托给原型的函数。

    // myObject constructor
    function myObject(foo, bar){
      this.foo = foo
      this.bar = bar
    }
    // delegate the functions to a prototype
    myObject.prototype.something = function(){
      console.log('something')
    }
    
    function instanceCopy(obj) {
      // copy the object by the constructor
      const copy = new obj.constructor()
      const keys = Object.keys(obj)
      keys.forEach(key => {
        copy[key] = obj[key]
      })
      return copy
    }
    
    const myObj = new myObject('foo', 'bar')
    const copyObj = instanceCopy(myObj)
    
    console.log('myObj', myObj)
    console.log('copyObj', copyObj)
    console.log('same ?', copyObj === myObj)
    
    // can we still call the functions
    copyObj.something()
    <script src="https://codepen.io/synthet1c/pen/WrQapG.js"></script>

    【讨论】:

      【解决方案5】:

      Lodash _.cloneDeep() 方法会扼杀应用程序的性能。所以我想出了基本的 JavaScript 解决方案。我已将它添加到我的 GIT 存储库中。使用我的解决方案后,我的应用程序性能恢复正常。

      https://github.com/manideeppabba1991/javascript_util_functions/blob/master/clone_Array_or_Object.js

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-07-03
        • 2012-06-13
        • 2011-07-25
        • 1970-01-01
        • 2015-01-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多