69 题: 在JavaScript中深度克隆对象的最有效方法是什么?

在...创建的问题 Wed, Mar 22, 2017 12:00 AM

克隆JavaScript对象的最有效方法是什么?我看过obj = eval(uneval(o));正在使用,但这是非标准的并且只有Firefox支持

我做过像obj = JSON.parse(JSON.stringify(o));这样的事情,但质疑效率。

我也看到了具有各种缺陷的递归复制函数。 点击 我很惊讶没有规范的解决方案。

    
5087
  1. Eval不是邪恶的。使用eval很差。如果你害怕它的副作用你使用它错了。您担心的副作用是使用它的原因。顺便提一下,有没有人回答你的问题?
    2012-03-22 14:08:54Z
  2. 克隆对象是一件棘手的事情,特别是对于任意集合的自定义对象。这可能就是为什么没有开箱即用的方法。
    2013-03-11 22:25:34Z
  3. eval()通常是一个坏主意,因为许多Javascript引擎的优化器在处理通过eval设置的变量时必须关闭。只需在代码中使用eval()就可能导致性能下降。
    2014-09-08 13:37:45Z
  4. 2016-02-21 18:21:08Z
  5. 请注意,JSON方法将丢失任何在JSON中没有等效的Javascript类型。例如:JSON.parse(JSON.stringify({a:null,b:NaN,c:Infinity,d:undefined,e:function(){},f:Number,g:false}))将生成{a: null, b: null, c: null, g: false}
    2017-05-24 13:06:47Z
  6. 醇>
    30答案                              30 跨度>                         
      

    2019年 - 6月注意:这最初是对另一个答案的回复,而不是对此问题的正确回答。不知道为什么它被选为正确的答案。但是,由于这个问题已经过去了,而且这是迄今为止这个问题的第一个答案,它将把解决方案概括为维基解答。

    原生深度克隆

    它被称为“结构化克隆”,在节点11及更高版本中实验性地工作,并且希望将落在浏览器中。请参见此回答了解更多详情。

    数据丢失的快速克隆 - JSON.parse /stringify

    如果您不在对象中使用Date s,函数,undefined,Infinity,RegExps,地图,集合,Blob,FileLists,ImageDatas,稀疏数组,类型化数组或其他复杂类型,则需要进行深度克隆一个对象是:

    JSON.parse(JSON.stringify(object))

     
    const a = {
      string: 'string',
      number: 123,
      bool: false,
      nul: null,
      date: new Date(),  // stringified
      undef: undefined,  // lost
      inf: Infinity,  // forced to 'null'
      re: /.*/,  // lost
    }
    console.log(a);
    console.log(typeof a.date);  // Date object
    const clone = JSON.parse(JSON.stringify(a));
    console.log(clone);
    console.log(typeof clone.date);  // result of .toISOString()

    参见 Corban对基准的回答

    使用库

    进行可靠的克隆

    由于克隆对象并不简单(复杂类型,循环引用,函数等),因此大多数主要库都提供克隆对象的功能。 不要重新发明轮子 - 如果您已经在使用库,请检查它是否具有对象克隆功能。例如,

    • lodash - cloneDeep ;可以通过 lodash.clonedeep 模块单独导入,如果你'可能是你的最佳选择尚未使用提供深度克隆功能的库
    • Angular - angular.copy
    • jQuery - jQuery.extend(true, { }, oldObject) ; .clone()只克隆DOM元素

    ES6

    为了完整起见,请注意ES6提供了两种浅拷贝机制: Object.assign() 传播运营商

        
    4311
    2019-06-14 02:29:00Z
    1. @ ThiefMaster 第276行的github.com/jquery/jquery/blob/master/src/core.js (除了“如何在JS中执行此操作”的代码之外,还有一些代码可以执行其他操作: )
      2013-03-27 08:16:19Z
    2. 这是jQuery深层拷贝背后的JS代码,对于任何感兴趣的人: github.com/jquery/jquery/blob/master/src/core.js#L265-327
      2013-04-11 14:24:13Z
    3. 哇!只是为了超级明确:不知道为什么这个回答被选为正确答案,这是对以下答复的回复: stackoverflow。 com /a /122190/6524 (推荐.clone(),这不是在这种情况下使用的正确代码)。不幸的是,这个问题已经经过了如此多的修改,原来的讨论已经不再明显了!如果您关心速度,请按照Corban的建议编写循环或将属性直接复制到新对象。或者亲自测试一下!
      2014-01-21 03:37:38Z
    4. 这是一个JavaScript问题(没有提到jQuery)。
      2015-01-22 08:30:39Z
    5. 如果不使用jQuery,如何做到这一点?
      2015-04-30 02:22:57Z
    6. 醇>

    查看此基准: http://jsben.ch/#/bWfk9

    在我之前的测试中,速度是我发现的一个主要问题

     
    JSON.parse(JSON.stringify(obj))
    

    是深度克隆对象的最慢方式(它慢于 jQuery.extend deep标志设置为10-20%。

    deep标志设置为false(浅层克隆)时,jQuery.extend非常快。这是一个很好的选择,因为它包含一些额外的类型验证逻辑,不会复制未定义的属性等,但这也会让你慢下来。

    如果你知道你想要克隆的对象的结构,或者可以避免深层嵌套数组,你可以编写一个简单的for (var i in obj)循环来克隆你的对象,同时检查hasOwnProperty,它将比jQuery快得多。

    最后,如果您尝试在热循环中克隆已知对象结构,只需简单地嵌入克隆过程并手动构建对象,即可获得更多性能。

    JavaScript跟踪引擎在优化for..in循环时很糟糕,并且检查hasOwnProperty也会减慢你的速度。当速度是绝对必须时手动克隆。

     
    var clonedObject = {
      knownProp: obj.knownProp,
      ..
    }
    

    小心在JSON.parse(JSON.stringify(obj))对象上使用Date方法 - JSON.stringify(new Date())以ISO格式返回日期的字符串表示形式,其中JSON.parse() 转换回Date对象。 有关详细信息,请参阅此答案一>

    此外,请注意,至少在Chrome 65中,本机克隆是不可取的。根据此JSPerf ,通过创建新功能执行本机克隆几乎 800x 比使用JSON.stringify要慢得多全程快速。

    ES6更新 强>

    如果您使用的是Javascript ES6,请尝试使用此本机方法进行克隆或浅层复制。

     
    Object.assign({}, obj);
    
        
    2179
    2019-06-21 11:52:59Z
    1. @ trysis Object.create没有克隆对象,正在使用原型对象... jsfiddle.net/rahpuser/yufzc1jt/2
      2014-11-25 21:37:12Z
    2. 此方法还将从keys中删除object,它们的值为functions,因为JSON不支持函数。
      2015-05-29 09:52:16Z
    3. 另请注意,在日期对象上使用JSON.parse(JSON.stringify(obj))也会将日期转换回 UTC ISO8601中的字符串表示形式格式。
      2015-07-30 21:37:10Z
    4. JSON方法也会对循环引用造成阻塞。
      2016-02-13 05:25:15Z
    5. @ velop,Object.assign({},objToClone)似乎它做了一个浅层克隆 - 在开发工具控制台中玩它时使用它,对象clone仍然指向克隆对象的引用。所以我不认为它在这里真的适用。
      2016-11-03 18:00:14Z
    6. 醇>

    假设您的对象中只有变量而不是任何函数,您可以使用:

     
    var newObject = JSON.parse(JSON.stringify(oldObject));
    
        
    452
    2014-10-23 23:47:23Z
    1. 这个方法的结果正如我刚刚发现的那样,如果你的对象有任何函数(我的内部有getter和setter),那么当字符串化时它们会丢失。如果这就是你所需要的,这个方法很好..
      2013-01-17 11:00:08Z
    2. @Jason,这种方法比浅层复制(在深层对象上)慢的原因是这种方法,根据定义,是深层复制。但是由于JSON是在本机代码中实现的(在大多数浏览器中),这将比使用任何其他基于javascript的深度复制解决方案快得多,并且可能有时比基于javascript的浅层复制技术更快(参见: jsperf.com/cloning-an-object/79 )。
      2013-07-04 07:42:37Z
    3. JSON.stringify({key: undefined}) //=> "{}"
      2014-04-30 06:24:36Z
    4. 这种技术也会破坏存储在对象中的所有Date个对象,将它们转换为字符串形式。
      2014-08-21 12:51:24Z
    5. 它将无法复制任何不属于JSON规范的内容( json.org
      2016-02-09 15:07:33Z
    6. 醇>

    结构化克隆

    HTML标准包括 内部结构化克隆/序列化算法 ,可以创建对象的深层克隆。它是still仅限于某些内置类型,但除了JSON支持的几种类型之外,它还支持日期,RegExps,地图,集合,Blob,文件列表,ImageDatas,稀疏数组,类型化数组,以及未来可能更多。它还保留了克隆数据中的引用,允许它支持可能导致JSON错误的循环和递归结构。

    Node.js中的支持:实验🙂

    Node.js中的v8模块当前(从节点11开始)直接公开结构化序列化API ,但此功能仍标记为“实验性”,并可能在将来的版本中更改或删除。如果您使用的是兼容版本,则克隆对象非常简单:

     
    const v8 = require('v8');
    
    const structuredClone = obj => {
      return v8.deserialize(v8.serialize(obj));
    };
    

    浏览器的直接支持:可能最终? 😐

    浏览器目前不提供结构化克隆算法的直接接口,但是structuredClone()函数。 noreferrer“>在GitHub上的whatwg /html#793 。正如目前提出的那样,在大多数情况下使用它将非常简单:

     
    const clone = structuredClone(original);
    

    除非发布此内容,否则浏览器的结构化克隆实现仅间接公开。

    异步解决方法:可用。 😕

    使用现有API创建结构化克隆的低开销方法是通过 MessageChannels 。另一个端口将使用附加的message的结构化克隆发出.data事件。不幸的是,监听这些事件必然是异步的,并且同步替代方案不太实用。

     
    class StructuredCloner {
      constructor() {
        this.pendingClones_ = new Map();
        this.nextKey_ = 0;
    
        const channel = new MessageChannel();
        this.inPort_ = channel.port1;
        this.outPort_ = channel.port2;
    
        this.outPort_.onmessage = ({data: {key, value}}) => {
          const resolve = this.pendingClones_.get(key);
          resolve(value);
          this.pendingClones_.delete(key);
        };
        this.outPort_.start();
      }
    
      cloneAsync(value) {
        return new Promise(resolve => {
          const key = this.nextKey_++;
          this.pendingClones_.set(key, resolve);
          this.inPort_.postMessage({key, value});
        });
      }
    }
    
    const structuredCloneAsync = window.structuredCloneAsync =
        StructuredCloner.prototype.cloneAsync.bind(new StructuredCloner);
    

    使用示例:

     
    const main = async () => {
      const original = { date: new Date(), number: Math.random() };
      original.self = original;
    
      const clone = await structuredCloneAsync(original);
    
      // They're different objects:
      console.assert(original !== clone);
      console.assert(original.date !== clone.date);
    
      // They're cyclical:
      console.assert(original.self === original);
      console.assert(clone.self === clone);
    
      // They contain equivalent values:
      console.assert(original.number === clone.number);
      console.assert(Number(original.date) === Number(clone.date));
    
      console.log("Assertions complete.");
    };
    
    main();
    

    同步解决方法:太可怕了! 🤢

    没有很好的选项可以同步创建结构化克隆。以下是一些不切实际的黑客攻击。

    history.pushState()history.replaceState()都创建了第一个参数的结构化克隆,并将该值分配给history.state。您可以使用它来创建任何对象的结构化克隆,如下所示:

     
    const structuredClone = obj => {
      const oldState = history.state;
      history.replaceState(obj, null);
      const clonedObj = history.state;
      history.replaceState(oldState, null);
      return clonedObj;
    };
    

    使用示例:

     
    'use strict';
    
    const main = () => {
      const original = { date: new Date(), number: Math.random() };
      original.self = original;
    
      const clone = structuredClone(original);
      
      // They're different objects:
      console.assert(original !== clone);
      console.assert(original.date !== clone.date);
    
      // They're cyclical:
      console.assert(original.self === original);
      console.assert(clone.self === clone);
    
      // They contain equivalent values:
      console.assert(original.number === clone.number);
      console.assert(Number(original.date) === Number(clone.date));
      
      console.log("Assertions complete.");
    };
    
    const structuredClone = obj => {
      const oldState = history.state;
      history.replaceState(obj, null);
      const clonedObj = history.state;
      history.replaceState(oldState, null);
      return clonedObj;
    };
    
    main();

    虽然是同步的,但这可能会非常慢。它会产生与操纵浏览器历史记录相关的所有开销。反复调用此方法可能会导致Chrome暂时无法响应。

    'use strict';
    
    const main = () => {
      const original = { date: new Date(), number: Math.random() };
      original.self = original;
    
      const clone = structuredClone(original);
      
      // They're different objects:
      console.assert(original !== clone);
      console.assert(original.date !== clone.date);
    
      // They're cyclical:
      console.assert(original.self === original);
      console.assert(clone.self === clone);
    
      // They contain equivalent values:
      console.assert(original.number === clone.number);
      console.assert(Number(original.date) === Number(clone.date));
      
      console.log("Assertions complete.");
    };
    
    const structuredClone = obj => {
      const oldState = history.state;
      history.replaceState(obj, null);
      const clonedObj = history.state;
      history.replaceState(oldState, null);
      return clonedObj;
    };
    
    main();
    构造函数创建了一个结构化克隆其相关数据。它还会尝试向用户显示浏览器通知,但除非您已请求通知权限,否则将以静默方式失败。如果您有其他目的的许可,我们将立即关闭我们创建的通知。

     Notification

    使用示例:

     
    const structuredClone = obj => {
      const n = new Notification('', {data: obj, silent: true});
      n.onshow = n.close.bind(n);
      return n.data;
    };
    
        
    335
    2019-02-05 02:21:46Z
    1. @ rynah我刚刚查看了规范,你说得对:
      'use strict';
      
      const main = () => {
        const original = { date: new Date(), number: Math.random() };
        original.self = original;
      
        const clone = structuredClone(original);
        
        // They're different objects:
        console.assert(original !== clone);
        console.assert(original.date !== clone.date);
      
        // They're cyclical:
        console.assert(original.self === original);
        console.assert(clone.self === clone);
      
        // They contain equivalent values:
        console.assert(original.number === clone.number);
        console.assert(Number(original.date) === Number(clone.date));
        
        console.log("Assertions complete.");
      };
      
      const structuredClone = obj => {
        const n = new Notification('', {data: obj, silent: true});
        n.close();
        return n.data;
      };
      
      main();
      'use strict';
      
      const main = () => {
        const original = { date: new Date(), number: Math.random() };
        original.self = original;
      
        const clone = structuredClone(original);
        
        // They're different objects:
        console.assert(original !== clone);
        console.assert(original.date !== clone.date);
      
        // They're cyclical:
        console.assert(original.self === original);
        console.assert(clone.self === clone);
      
        // They contain equivalent values:
        console.assert(original.number === clone.number);
        console.assert(Number(original.date) === Number(clone.date));
        
        console.log("Assertions complete.");
      };
      
      const structuredClone = obj => {
        const n = new Notification('', {data: obj, silent: true});
        n.close();
        return n.data;
      };
      
      main();
      方法同步将history.pushState()设置为第一个参数的结构化克隆。有点奇怪,但它的工作原理。我现在正在更新我的答案。
      2013-05-06 03:11:03Z
    2. 这是错的!该API并不意味着以这种方式使用。
      2014-07-31 23:34:51Z
    3. 作为在Firefox中实现pushState的人,我觉得这个黑客的骄傲和反感奇怪。干得好,伙计。
      2014-08-14 18:37:25Z
    4. 醇>

    如果没有内置的,你可以尝试:

     history.replaceState()     
    309
    2018-09-27 08:59:03Z
    1. JQuery解决方案适用于DOM元素,但不适用于任何Object。 Mootools具有相同的限制。希望他们有一个通用“克隆”只适用于任何对象...递归解决方案应该适用于任何事情。这可能是要走的路。
      2008-09-23 17:23:51Z
    2. 如果被克隆的对象具有需要参数的构造函数,则此函数会中断。看起来我们可以将它改为“var temp = new Object()”并让它在每种情况下都有效,不是吗?
      2009-10-04 22:06:39Z
    3. Andrew,如果将其更改为var temp = new Object(),那么您的克隆将不会具有与原始对象相同的原型。尝试使用:'var newProto = function(){}; newProto.prototype = obj.constructor; var temp = new newProto();'
      2011-09-14 15:53:31Z
    4. 与limscoder的答案类似,请参阅下面的答案,了解如何在不调用构造函数的情况下执行此操作:stackoverflow.com/a/13333781/560114
      2012-11-11 17:55:03Z
    5. 对于包含对子部分(即对象网络)的引用的对象,这不起作用:如果两个引用指向同一个子对象,则副本包含两个不同的副本。如果有递归引用,函数将永远不会终止(好吧,至少不是你想要它的方式:-)对于这些一般情况,你必须添加一个已经复制的对象的字典,并检查你是否已经复制它...当您使用简单的语言
      时,编程很复杂
      2013-11-11 08:34:37Z
    6. 醇>

    在一行代码中克隆(不深度克隆)对象的有效方法

    history.state 方法属于ECMAScript 2015(ES6)标准,完全满足您的需求。

     
    function clone(obj) {
        if (obj === null || typeof (obj) !== 'object' || 'isActiveClone' in obj)
            return obj;
    
        if (obj instanceof Date)
            var temp = new obj.constructor(); //or new Date(obj);
        else
            var temp = obj.constructor();
    
        for (var key in obj) {
            if (Object.prototype.hasOwnProperty.call(obj, key)) {
                obj['isActiveClone'] = null;
                temp[key] = clone(obj[key]);
                delete obj['isActiveClone'];
            }
        }
        return temp;
    }
    
      

    Object.assign()方法用于将所有可枚举的自有属性的值从一个或多个源对象复制到目标对象。

    了解详情......

    支持旧浏览器的 polyfill

     Object.assign     
    154
    2017-02-14 08:46:58Z
    1. 这不会递归复制,因此无法真正解决克隆对象的问题。
      2016-03-08 19:56:53Z
    2. 这个方法有效,虽然我测试了一些而且_.extend({},(obj))是最快的BY FAR:比JSON.parse和60快20倍例如,比Object.assign快%。它很好地复制了所有子对象。
      2016-05-09 19:57:22Z
    3. @ mwhite clone和deep-clone之间存在差异。这个答案实际上是克隆,但它没有深度克隆。
      2016-06-08 12:08:03Z
    4. op要求深度克隆。这不做深度克隆。
      2016-07-31 03:49:03Z
    5. 哇这么多的Object.assign响应甚至没有读过op的问题......
      2017-12-29 10:02:16Z
    6. 醇>

    代码:

     
    var clone = Object.assign({}, obj);
    

    测试:

     
    if (!Object.assign) {
      Object.defineProperty(Object, 'assign', {
        enumerable: false,
        configurable: true,
        writable: true,
        value: function(target) {
          'use strict';
          if (target === undefined || target === null) {
            throw new TypeError('Cannot convert first argument to object');
          }
    
          var to = Object(target);
          for (var i = 1; i < arguments.length; i++) {
            var nextSource = arguments[i];
            if (nextSource === undefined || nextSource === null) {
              continue;
            }
            nextSource = Object(nextSource);
    
            var keysArray = Object.keys(nextSource);
            for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
              var nextKey = keysArray[nextIndex];
              var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
              if (desc !== undefined && desc.enumerable) {
                to[nextKey] = nextSource[nextKey];
              }
            }
          }
          return to;
        }
      });
    }
    
        
    96
    2011-03-26 14:20:09Z
    1. // extends 'from' object with members from 'to'. If 'to' is null, a deep clone of 'from' is returned
      function extend(from, to)
      {
          if (from == null || typeof from != "object") return from;
          if (from.constructor != Object && from.constructor != Array) return from;
          if (from.constructor == Date || from.constructor == RegExp || from.constructor == Function ||
              from.constructor == String || from.constructor == Number || from.constructor == Boolean)
              return new from.constructor(from);
      
          to = to || new from.constructor();
      
          for (var name in from)
          {
              to[name] = typeof to[name] == "undefined" ? extend(from[name], null) : to[name];
          }
      
          return to;
      }
      
      var obj =
      {
          date: new Date(),
          func: function(q) { return 1 + q; },
          num: 123,
          text: "asdasd",
          array: [1, "asd"],
          regex: new RegExp(/aaa/i),
          subobj:
          {
              num: 234,
              text: "asdsaD"
          }
      }
      
      var clone = extend(obj);
      
      怎么样?
      2015-05-05 22:40:00Z
    2. 我不明白这个功能。例如,假设var obj = {}obj.a = obj。当第二次from.constructor测试成功时,第三次Date测试将如何达到?导致函数返回(自if)?
      2015-09-01 02:10:41Z
    3. @ AdamMcKee因为 javascript参数传递和变量赋值很棘手。这种方法效果很好,包括日期(确实由第二次测试处理) - 在这里测试小提琴: jsfiddle.net/zqv9q9c6
      2016-06-14 20:39:15Z
    4. *我的意思是“参数赋值”(在函数体内),而不是“变量赋值”。虽然它有助于彻底理解这两者。
      2016-06-15 16:42:40Z
    5. @ NickSweeting:试试 - 可能有效。如果没有 - 修复它并更新答案。这就是它在社区中的工作方式:)
      2017-07-10 07:20:58Z
    6. 醇>

    这就是我正在使用的:

     if     
    92
    2013-07-16 16:37:34Z
    1. 这似乎不对。 Date != Object && Date != Array =&gt;
      function cloneObject(obj) {
          var clone = {};
          for(var i in obj) {
              if(typeof(obj[i])=="object" && obj[i] != null)
                  clone[i] = cloneObject(obj[i]);
              else
                  clone[i] = obj[i];
          }
          return clone;
      }
      
      2013-02-27 14:36:45Z
    2. 这是由于javascript cloneObject({ name: null })中的另一个愚蠢的事情,但{"name":{}}将条件更改为typeof null > "object"
      2013-04-16 16:42:45Z
    3. 这会将 obj 的继承可枚举属性直接分配给克隆,并假设 obj 是普通对象
      2016-02-01 22:58:11Z
    4. 这也会混淆数组,这些数组会被转换为带有数字键的对象。
      2016-11-03 12:29:03Z
    5. 如果不使用null,则不成问题。
      2017-03-01 06:17:44Z
    6. 醇>

    按效果进行深层复制 排名从最佳到最差

    • 重新分配“=”(字符串数组,仅数字数组)
    • 切片(字符串数组,仅数字数组)
    • 连接(字符串数组,仅数字数组)
    • 自定义功能:for-loop或递归复制
    • jQuery的$.extend
    • JSON.parse(字符串数组,数组数组,仅限对象数组)
    • Underscore.js 的_.clone(字符串数组,仅数字数组)
    • Lo-Dash的_.cloneDeep

    深层复制字符串或数字数组(一级 - 无参考指针):

    当数组包含数字和字符串时 - 函数如.slice(),. concat(),. splice(),赋值运算符“=”和Underscore.js的克隆函数;将制作数组元素的深层副本。

    重新分配的速度最快:

     Object.keys(null) > TypeError: Requested keys of a value that is not an object.

    .slice()的性能优于.concat(), http://jsperf.com/duplicate-array-slice-vs-concat/3

     if(typeof(obj[i])=="object" && obj[i]!=null)

    深层复制一个对象数组(两个或多个关卡 - 引用指针):

     
    var arr1 = ['a', 'b', 'c'];
    var arr2 = arr1;
    arr1 = ['a', 'b', 'c'];
    

    编写自定义函数(性能比$.extend()或JSON.parse快):

     
    var arr1 = ['a', 'b', 'c'];  // Becomes arr1 = ['a', 'b', 'c']
    var arr2a = arr1.slice(0);   // Becomes arr2a = ['a', 'b', 'c'] - deep copy
    var arr2b = arr1.concat();   // Becomes arr2b = ['a', 'b', 'c'] - deep copy
    

    使用第三方实用程序功能蒸发散:

     
    var arr1 = [{object:'a'}, {object:'b'}];
    

    jQuery的$.extend具有更好的性能:

    77
    2018-03-20 15:11:04Z
    1. 我测试了一些而_.extend({},(obj))是最快的BY FAR:比JSON.parse快20倍,比Object慢60%。例如,分配。它很好地复制了所有子对象。
      2016-05-09 19:56:02Z
    2. @ NicoDurand - 您的性能测试是否在线?
      2016-05-17 13:36:31Z
    3. 你的所有例子都很浅,一层。这不是一个好的答案。该问题涉及克隆,即至少两个级别。
      2017-04-21 10:02:14Z
    4. 深度复制是指在不使用对其他对象的引用指针的情况下完整复制对象的时间。 “深度复制对象数组”部分中的技术,例如jQuery.extend()和自定义函数(递归),复制具有“至少两个级别”的对象。所以,并非所有的例子都是“一级”副本。
      2017-04-21 15:42:01Z
    5. 我喜欢你的自定义复制函数,但你应该排除空值,否则所有空值都被转换为对象,即:
      function copy(o) {
         var out, v, key;
         out = Array.isArray(o) ? [] : {};
         for (key in o) {
             v = o[key];
             out[key] = (typeof v === "object" && v !== null) ? copy(v) : v;
         }
         return out;
      }
      
      copy(arr1);
      
      2018-02-01 16:53:21Z
    6. 醇>
     
    $.extend(true, [], arr1); // Jquery Extend
    JSON.parse(arr1);
    _.cloneDeep(arr1); // Lo-dash
    
        
    62
    2013-02-21 15:01:15Z

    有一个库(称为“克隆”),可以很好地完成这项工作。它提供了我所知道的最完整的递归克隆/复制任意对象。它还支持循环引用,但尚未涵盖其他答案。

    您也可以在npm 找到它。它可以用于浏览器以及Node.js。

    以下是如何使用它的示例:

    安装  out[key] = (typeof v === "object" && v !== null) ? copy(v) : v;

    或使用 Ender 打包。

     
    var clone = function() {
        var newObj = (this instanceof Array) ? [] : {};
        for (var i in this) {
            if (this[i] && typeof this[i] == "object") {
                newObj[i] = this[i].clone();
            }
            else
            {
                newObj[i] = this[i];
            }
        }
        return newObj;
    }; 
    
    Object.defineProperty( Object.prototype, "clone", {value: clone, enumerable: false});
    

    您也可以手动下载源代码。

    然后您可以在源代码中使用它。

     
    npm install clone
    

    (免责声明:我是图书馆的作者。)

        
    55
    2013-03-27 08:04:54Z
    1. npm clone对于克隆任意嵌套对象非常宝贵。这是正确的答案。
      2015-09-10 03:21:23Z
    2. 与让我们说
      ender build clone [...]
      
      相比,你的lib的性能是什么?
      2016-12-01 16:03:35Z
    3. 这是一个图书馆表示有更快的选择。尚未测试过。
      2016-12-01 21:07:34Z
    4. 醇>

    我知道这是一个老帖子,但我认为这可能对下一个偶然发现的人有所帮助。

    只要你不指定o在内存中没有任何引用的任何东西。因此,要创建一个您想要在其他对象之间共享的对象,您必须创建一个这样的工厂:

     
    var clone = require('clone');
    
    var a = { foo: { bar: 'baz' } };  // inital value of a
    var b = clone(a);                 // clone a -> b
    a.foo.bar = 'foo';                // change a
    
    console.log(a);                   // { foo: { bar: 'foo' } }
    console.log(b);                   // { foo: { bar: 'baz' } }
    
        
    52
    2015-03-17 01:18:29Z
    1. 这个答案并不真正相关,因为问题是:给定实例b如何创建副本c何时不知道工厂a或不想使用工厂a。可能不想使用工厂的原因是在实例化之后b可能已经用附加数据(例如用户输入)初始化。
      2012-05-08 09:57:53Z
    2. 这确实不是这个问题的答案,但我认为它在这里很重要,因为它是我怀疑很多问题的答案。来这里的人真的很有意思。
      2013-03-06 23:31:03Z
    3. 对不起伙计们,我真的不明白为什么这么多的赞成。克隆一个对象是一个非常清晰的概念,你可以从另一个对象中对一个对象进行对比,并且它与使用工厂模式创建一个新对象没什么关系。
      2014-08-18 12:51:28Z
    4. 虽然这适用于预定义对象,但以这种方式“克隆”将无法识别添加到原始对象的新属性。如果您创建了一个,则向a添加一个新属性,然后创建b。 b不会有新房产。本质上,工厂模式对于新属性是不可变的。这不是范例克隆。请参阅: jsfiddle.net/jzumbrun/42xejnbx
      2016-09-03 13:59:46Z
    5. 我认为这是一个很好的建议,一般来说,因为不是使用JSON.parse(JSON.stringify(obj)),你可以去
      var a = function(){
          return {
              father:'zacharias'
          };
      },
      b = a(),
      c = a();
      c.father = 'johndoe';
      alert(b.father);
      
      ,你的问题就解决了。但是,它确实不是问题的答案。作为对问题的评论可能更有意义,而不是完整的答案。
      2017-02-06 14:25:41Z
    6. 醇>

    const defaultFoo = { a: { b: 123 } };一个对象在JS中始终是一个问题,但它完全是在ES6之前,我在下面列出了在JavaScript中复制对象的不同方法,想象你有下面的Object,并希望有一个深层副本:

     const defaultFoo = () => ({ a: { b: 123 } };

    有几种方法可以复制此对象,而无需更改原点:

    1)ES5 +,使用简单的功能为您复制:

     Cloning

    2)ES5 +,使用JSON.parse和JSON.stringify。

     
    var obj = {a:1, b:2, c:3, d:4};
    

    3)AngularJs:

     
    function deepCopyObj(obj) {
        if (null == obj || "object" != typeof obj) return obj;
        if (obj instanceof Date) {
            var copy = new Date();
            copy.setTime(obj.getTime());
            return copy;
        }
        if (obj instanceof Array) {
            var copy = [];
            for (var i = 0, len = obj.length; i < len; i++) {
                copy[i] = cloneSO(obj[i]);
            }
            return copy;
        }
        if (obj instanceof Object) {
            var copy = {};
            for (var attr in obj) {
                if (obj.hasOwnProperty(attr)) copy[attr] = cloneSO(obj[attr]);
            }
            return copy;
        }
        throw new Error("Unable to copy obj this object.");
    }
    

    4)jQuery:

     
    var  deepCopyObj = JSON.parse(JSON.stringify(obj));
    

    5)UnderscoreJs&amp; Loadash:

     
    var  deepCopyObj = angular.copy(obj);
    

    希望这些帮助...

        
    52
    2018-02-24 17:35:02Z
    1. 下划线中的克隆不是当前版本中的深层克隆
      2017-04-03 16:19:25Z
    2. 谢谢。是作为Underscore的新文档... clone_.clone(object)创建提供的普通对象的浅复制克隆。任何嵌套对象或数组都将通过引用复制,而不是重复。 _.clone({name:'moe'}); =&GT; {name:'moe'};
      2017-04-04 01:00:44Z
    3. var deepCopyObj = jQuery.extend(true, {}, obj);
      
      深拷贝。示例:
      var deepCopyObj = _.cloneDeep(obj); //latest version UndescoreJs makes shallow copy
      
      。如果这是深拷贝,Object.assign仍然是var x = { a: { b: "c" } }; var y = Object.assign({}, x); x.a.b = "d",但它现在是y.a.b
      2017-05-10 15:52:55Z
    4. Object.assign()只克隆第一级属性!
      2017-07-21 10:13:53Z
    5. 什么是cloneSO()函数?
      2017-08-07 16:55:40Z
    6. 醇>

    如果您正在使用它, Underscore.js 库中的克隆方法。

     c     
    48
    2016-07-22 17:26:12Z
    1. lodash有一个cloneDeep方法,它还支持另一个克隆来进行深度修改: lodash.com/docs#clone lodash.com/docs#cloneDeep
      2013-03-02 17:14:53Z
    2. @ opensas同意。 Lodash通常优于下划线
      2014-07-31 12:12:37Z
    3. 我主张删除这个以及所有其他答案,这些答案只是对实用程序库d方法的单行引用。每个主要的图书馆都会有它们,并且重复的简短非详细答案对大多数访问者都没有用,他们不会使用该特定的图书馆。
      2016-04-07 17:25:25Z
    4. 醇>

    在JavaScript中深度复制对象(我认为最好也是最简单的)

    1。使用JSON.parse(JSON.stringify(object));

     
    var newObject = _.clone(oldObject);
    

    2.使用创建的方法

     .clone(...)

    第3。使用Lo-Dash的_.cloneDeep 链接 lodash

     
    var obj = { 
      a: 1,
      b: { 
        c: 2
      }
    }
    var newObj = JSON.parse(JSON.stringify(obj));
    obj.b.c = 20;
    console.log(obj); // { a: 1, b: { c: 20 } }
    console.log(newObj); // { a: 1, b: { c: 2 } } 
    

    4。使用Object.assign()方法

     
    function cloneObject(obj) {
        var clone = {};
        for(var i in obj) {
            if(obj[i] != null &&  typeof(obj[i])=="object")
                clone[i] = cloneObject(obj[i]);
            else
                clone[i] = obj[i];
        }
        return clone;
    }
    
    var obj = { 
      a: 1,
      b: { 
        c: 2
      }
    }
    var newObj = cloneObject(obj);
    obj.b.c = 20;
    
    console.log(obj); // { a: 1, b: { c: 20 } }
    console.log(newObj); // { a: 1, b: { c: 2 } } 
    

    但是错误

     
    var obj = { 
      a: 1,
      b: { 
        c: 2
      }
    }
    
    var newObj = _.cloneDeep(obj);
    obj.b.c = 20;
    console.log(obj); // { a: 1, b: { c: 20 } }
    console.log(newObj); // { a: 1, b: { c: 2 } } 
    

    5.使用Underscore.js _.clone 链接 Underscore.js  

    var obj = { 
      a: 1,
      b: 2
    }
    
    var newObj = _.clone(obj);
    obj.b = 20;
    console.log(obj); // { a: 1, b: 20 }
    console.log(newObj); // { a: 1, b: 2 }  
    

    但是错误

     
    var obj = { 
      a: 1,
      b: { 
        c: 2
      }
    }
    
    var newObj = Object.assign({}, obj);
    obj.b.c = 20;
    console.log(obj); // { a: 1, b: { c: 20 } }
    console.log(newObj); // { a: 1, b: { c: 20 } } --> WRONG
    // Note: Properties on the prototype chain and non-enumerable properties cannot be copied.
    

    参考medium.com

    JSBEN.CH Performance Benchmarking Playground 1~3 http://jsben.ch/KVQLd

        
    43
    2018-10-31 01:25:49Z
    1. var obj = { 
        a: 1,
        b: 2
      }
      
      var newObj = _.clone(obj);
      obj.b = 20;
      console.log(obj); // { a: 1, b: 20 }
      console.log(newObj); // { a: 1, b: 2 }  
      
      不执行深层复制
      2018-08-15 20:23:28Z
    2. 你应该为这些添加基准;这将是非常有帮助的
      2018-10-30 23:00:19Z
    3. 当我在包含数组的对象上使用“created method”时我无法使用pop()或splice(),我不明白为什么?
      var obj = { 
        a: 1,
        b: { 
          c: 2
        }
      }
      
      var newObj = _.cloneDeep(obj);
      obj.b.c = 20;
      console.log(obj); // { a: 1, b: { c: 20 } }
      console.log(newObj); // { a: 1, b: { c: 20 } } --> WRONG
      // (Create a shallow-copied clone of the provided plain object. Any nested objects or arrays will be copied by reference, not duplicated.)
      
      它抛出:Object.assign()(当然pop()工作正常,如果我只是let data = {title:["one", "two"]}; let tmp = cloneObject(data); tmp.title.pop();;但那时我不能修改tmp而不影响数据)
      2019-03-24 23:54:35Z
    4. 醇>

    以上是ConroyP上面的答案的一个版本,即使构造函数需要参数也可以使用:

     TypeError: tmp.title.pop is not a function

    此功能也可在我的 simpleoo 库中找到。

    修改强>

    这是一个更强大的版本(感谢Justin McCandless,现在它也支持循环引用):

     do let tmp = data     
    38
    2017-03-12 18:02:45Z

    以下内容创建了同一对象的两个实例。我发现它并且正在使用它。它简单易用。

     
    //If Object.create isn't already defined, we just do the simple shim,
    //without the second argument, since that's all we need here
    var object_create = Object.create;
    if (typeof object_create !== 'function') {
        object_create = function(o) {
            function F() {}
            F.prototype = o;
            return new F();
        };
    }
    
    function deepCopy(obj) {
        if(obj == null || typeof(obj) !== 'object'){
            return obj;
        }
        //make sure the returned object has the same prototype as the original
        var ret = object_create(obj.constructor.prototype);
        for(var key in obj){
            ret[key] = deepCopy(obj[key]);
        }
        return ret;
    }
    
        
    31
    2016-12-01 16:06:53Z
    1. 这个答案有什么问题吗?作为一个独立的解决方案,它更有用,但更简单;但jQuery解决方案更受欢迎。那是为什么?
      2015-09-24 17:15:39Z
    2. 是的,请告诉我。它似乎按预期工作,如果在某处有一些隐藏的破损,我需要使用不同的解决方案。
      2015-09-25 15:34:58Z
    3. 对于一个简单的对象,Chrome的速度比给定的答案慢大约6倍,随着对象的复杂性增加,速度会慢得多。它可以非常快速地扩展并且可以很快地阻塞你的应用程序。
      2015-12-08 23:10:3​​7Z
    4. 您不需要数据,只需要了解正在发生的事情。此克隆技术将整个对象序列化为字符串,然后解析该字符串序列化以构建对象。本质上,它只是比简单地重新安排一些内存要慢得多(这是更复杂的克隆所做的事情)。但话虽如此,对于中小型项目(取决于您对“中型”的定义)谁关心它的效率是否甚至低1000倍?如果你的对象很小并且你没有克隆它们,那么几乎没有任何东西实际上没有任何东西。
      2016-12-19 20:20:26Z
    5. 此外,此方法丢失方法(或JSON中不允许的任何内容),加上 - JSON.stringify将Date对象转换为字符串,...而不是反过来;)坚持这个解决方案。
      2017-08-11 22:11:09Z
    6. 醇>

    Lodash有一个很好的 _.cloneDeep(value)方法:

     
    /**
     * Deep copy an object (make copies of all its object properties, sub-properties, etc.)
     * An improved version of http://keithdevens.com/weblog/archive/2007/Jun/07/javascript.clone
     * that doesn't break if the constructor has required parameters
     * 
     * It also borrows some code from http://stackoverflow.com/a/11621004/560114
     */ 
    function deepCopy(src, /* INTERNAL */ _visited, _copiesVisited) {
        if(src === null || typeof(src) !== 'object'){
            return src;
        }
    
        //Honor native/custom clone methods
        if(typeof src.clone == 'function'){
            return src.clone(true);
        }
    
        //Special cases:
        //Date
        if(src instanceof Date){
            return new Date(src.getTime());
        }
        //RegExp
        if(src instanceof RegExp){
            return new RegExp(src);
        }
        //DOM Element
        if(src.nodeType && typeof src.cloneNode == 'function'){
            return src.cloneNode(true);
        }
    
        // Initialize the visited objects arrays if needed.
        // This is used to detect cyclic references.
        if (_visited === undefined){
            _visited = [];
            _copiesVisited = [];
        }
    
        // Check if this object has already been visited
        var i, len = _visited.length;
        for (i = 0; i < len; i++) {
            // If so, get the copy we already made
            if (src === _visited[i]) {
                return _copiesVisited[i];
            }
        }
    
        //Array
        if (Object.prototype.toString.call(src) == '[object Array]') {
            //[].slice() by itself would soft clone
            var ret = src.slice();
    
            //add it to the visited array
            _visited.push(src);
            _copiesVisited.push(ret);
    
            var i = ret.length;
            while (i--) {
                ret[i] = deepCopy(ret[i], _visited, _copiesVisited);
            }
            return ret;
        }
    
        //If we've reached here, we have a regular object
    
        //make sure the returned object has the same prototype as the original
        var proto = (Object.getPrototypeOf ? Object.getPrototypeOf(src): src.__proto__);
        if (!proto) {
            proto = src.constructor.prototype; //this line would probably only be reached by very old browsers 
        }
        var dest = object_create(proto);
    
        //add this object to the visited array
        _visited.push(src);
        _copiesVisited.push(dest);
    
        for (var key in src) {
            //Note: this does NOT preserve ES5 property attributes like 'writable', 'enumerable', etc.
            //For an example of how this could be modified to do so, see the singleMixin() function
            dest[key] = deepCopy(src[key], _visited, _copiesVisited);
        }
        return dest;
    }
    
    //If Object.create isn't already defined, we just do the simple shim,
    //without the second argument, since that's all we need here
    var object_create = Object.create;
    if (typeof object_create !== 'function') {
        object_create = function(o) {
            function F() {}
            F.prototype = o;
            return new F();
        };
    }
    
        
    23
    2017-12-31 12:59:40Z
    1. 我主张删除这个以及所有其他答案,这些答案只是对实用程序库
      var objToCreate = JSON.parse(JSON.stringify(cloneThis));
      
      方法的单行引用。每个主要的图书馆都会有它们,并且重复的简短非详细答案对大多数访问者都没有用,他们不会使用该特定的图书馆。
      2016-04-07 17:23:49Z
    2. 更简单的方法是使用
      var objects = [{ 'a': 1 }, { 'b': 2 }];
      
      var deep = _.cloneDeep(objects);
      console.log(deep[0] === objects[0]);
      // => false
      
      。如果只有lodash没有首先改变对象,则不需要.clone(...)函数。
      2017-02-27 00:48:02Z
    3. Google搜索克隆JS对象请参考此处。我正在使用Lodash所以这个答案对我来说很重要。让我们不要回答所有“wikipedia删除主义者”的答案。
      2017-02-27 00:49:11Z
    4. 在节点9中,JSON.parse(JSON.stringify(arrayOfAbout5KFlatObjects))比_.deepClone(arrayOfAbout5KFlatObjects)快得多。
      2017-12-31 13:07:15Z
    5. 醇>
     _.merge({}, objA)     
    22
    2008-09-23 16:45:39Z
    1. 方法的问题是,如果你在obj中有子对象,它们的引用将被克隆,而不是每个子对象的值。
      2009-06-25 07:46:29Z
    2. 只需使其递归,以便深入克隆子对象。
      2013-01-25 22:38:29Z
    3. 只是好奇...不会是克隆变量会有指向原始对象属性的指针吗?因为它似乎没有新的内存分配
      2013-02-04 09:42:17Z
    4. 是的。这只是一个浅表副本,因此克隆将指向原始对象指向的完全相同的对象。
      2013-02-04 10:18:09Z
    5. 这不是答案。你的字面意思只是填充一个引用另一个对象的对象。对源对象进行更改将更改“clone”。
      2019-03-20 17:34:58Z
    6. 醇>

    Crockford建议(我更喜欢)使用此功能:

     clone

    它很简洁,按预期工作,你不需要图书馆。


    修改强>

    这是

    function clone(obj)
     { var clone = {};
       clone.prototype = obj.prototype;
       for (property in obj) clone[property] = obj[property];
       return clone;
     }
    
    的polyfill,所以你也可以使用它。  
    function object(o) {
        function F() {}
        F.prototype = o;
        return new F();
    }
    
    var newObject = object(oldObject);
    

    注意:如果你使用其中一些,你可能会遇到使用Object.create的一些迭代问题。因为,

    var newObject = Object.create(oldObject);
    
    创建了继承hasOwnProperty的新空对象。但它对于克隆对象仍然有用且实用。

    例如create

     oldObject

    但:

     oldObject.a = 5;     
    22
    2016-03-12 14:59:41Z
    1. 如果我错了,请纠正我,但这不是Crockford的原型继承功能吗?它如何适用于克隆?
      2010-10-06 15:17:18Z
    2. 是的,我害怕这个讨论:克隆,复制和原型继承之间的实际区别是什么,你何时应该使用它们以及这个页面上的哪些函数实际上是做什么?我通过Google搜索“javascript copy object”找到了这个SO页面。我真正想要的是上面的功能,所以我回来分享。我的猜测是提问者也在寻找这个。
      2010-10-06 19:51:48Z
    3. 克隆/复制和继承之间的区别在于 - 使用您的示例,当我更改oldObject的属性时,该属性也会在newObject中更改。如果您制作副本,则可以使用oldObject执行所需操作而无需更改newObject。
      2010-12-06 13:13:26Z
    4. 这将打破hasOwnProperty检查,因此它是克隆对象的一种非常黑客的方法,会给你带来意想不到的结果。
      2011-03-16 18:17:18Z
    5. 2014-04-24 18:32:03Z
    6. 醇>

    浅拷贝单行( ECMAScript第5版):

     
    oldObject.hasOwnProperty(a); // is true
    newObject.hasOwnProperty(a); // is false
    

    浅拷贝单行( ECMAScript第6版,2015):

     var extendObj = function(childObj, parentObj) { var tmpObj = function () {} tmpObj.prototype = parentObj.prototype; childObj.prototype = new tmpObj(); childObj.prototype.constructor = childObj; };     
    19
    2016-07-22 17:27:51Z
    1. 这对于简单对象可能没问题,但它只复制属性值。它不会触及原型链,并且通过使用
      var origin = { foo : {} };
      var copy = Object.keys(origin).reduce(function(c,k){c[k]=origin[k];return c;},{});
      
      console.log(origin, copy);
      console.log(origin == copy); // false
      console.log(origin.foo == copy.foo); // true
      
      ,它会跳过不可枚举和继承的属性。此外,它通过直接赋值来丢失属性描述符。
      2013-11-23 17:51:52Z
    2. 如果您也复制原型,那么您只会丢失非枚举和属性描述符,是吗?非常好。 :)
      2014-06-08 06:24:04Z
    3. 除了性能之外,这是浅层复制对象的一种非常方便的方法。我经常使用它来在我的React组件中的解构赋值中对假的rest属性进行排序。
      2016-03-17 13:56:48Z
    4. 醇>

    仅仅因为我没有看到提到的 AngularJS 并认为人们可能想知道。 ..

    var origin = { foo : {} };
    var copy = Object.assign({}, origin);
    
    console.log(origin, copy);
    console.log(origin == copy); // false
    console.log(origin.foo == copy.foo); // true
    
    还提供了深度复制对象和数组的方法。

        
    17
    2016-10-15 18:38:31Z
    1. 或者它可能与jQiery扩展使用的方式相同:Object.keys
      2016-09-21 09:07:42Z
    2. @ Galvani:应该注意angular.copyangular.extend({},obj);都是浅拷贝。 jQuery.extend是深层拷贝。
      2016-10-15 18:41:00Z
    3. 醇>

    对于类似数组的对象,似乎还没有理想的深度克隆运算符。如下面的代码所示,John Resig的jQuery克隆器将具有非数字属性的数组转换为非数组的对象,RegDwight的JSON克隆器删除非数字属性。以下测试在多个浏览器上说明了这些要点:

     angular.extend     
    16
    2013-12-13 19:00:03Z
    1. 正如其他人在对Resig的回答的评论中指出的那样,如果你想克隆类似数组的对象,你可以在扩展调用中将{}更改为[],例如jQuery.extend(true,[],obj)
      2011-03-03 21:27:05Z
    2. angular.copy不适用于
      function jQueryClone(obj) {
         return jQuery.extend(true, {}, obj)
      }
      
      function JSONClone(obj) {
         return JSON.parse(JSON.stringify(obj))
      }
      
      var arrayLikeObj = [[1, "a", "b"], [2, "b", "a"]];
      arrayLikeObj.names = ["m", "n", "o"];
      var JSONCopy = JSONClone(arrayLikeObj);
      var jQueryCopy = jQueryClone(arrayLikeObj);
      
      alert("Is arrayLikeObj an array instance?" + (arrayLikeObj instanceof Array) +
            "\nIs the jQueryClone an array instance? " + (jQueryCopy instanceof Array) +
            "\nWhat are the arrayLikeObj names? " + arrayLikeObj.names +
            "\nAnd what are the JSONClone names? " + JSONCopy.names)
      
      2016-03-12 15:08:19Z
    3. 醇>

    根据您的目标是否克隆“普通的旧JavaScript对象”,我有两个很好的答案。

    让我们假设你的目的是创建一个完整的克隆,没有原型引用回到源对象。如果你对一个完整的克隆不感兴趣,那么你可以使用其他一些答案中提供的许多Object.clone()例程(Crockford的模式)。

    对于普通的旧JavaScript对象,在现代运行时克隆对象的一种经过验证的好方法很简单:

     JSON.stringify

    请注意,源对象必须是纯JSON对象。这就是说,它的所有嵌套属性都必须是标量(如boolean,string,array,object等)。任何函数或特殊对象(如RegExp或Date)都不会被克隆。

    有效吗?哎呀。我们已经尝试了各种克隆方法,这种方法效果最好。我相信一些忍者可以想出一个更快的方法。但我怀疑我们谈论的是边际收益。

    这种方法简单易行。将它包装成一个便利功能,如果你真的需要挤出一些收益,请稍后再去。

    现在,对于非纯JavaScript对象,没有一个非常简单的答案。实际上,由于JavaScript函数和内部对象状态的动态特性,不可能存在。深入克隆具有内部函数的JSON结构需要重新创建这些函数及其内部上下文。而JavaScript根本没有标准化的方法。

    再次执行此操作的正确方法是通过在代码中声明和重用的便捷方法。方便的方法可以让您对自己的对象有所了解,这样您就可以确保在新对象中正确地重新创建图形。

    我们写的是自己的,但我所看到的最好的一般方法都在这里讨论:

    http://davidwalsh.name/javascript-clone

    这是正确的想法。作者(David Walsh)评论了广义函数的克隆。根据您的使用情况,您可以选择这样做。

    主要思想是你需要在每个类型的基础上特殊处理你的函数(或者原型类)的实例化。在这里,他提供了几个RegExp和Date的例子。

    此代码不仅简短,而且非常易读。这很容易扩展。

    效率这么高吗?哎呀。鉴于目标是生成真正的深拷贝克隆,那么您将不得不遍历源对象图的成员。通过这种方法,您可以确切地调整要处理的子成员以及如何手动处理自定义类型。

    所以你去吧。两种方法。在我看来,两者都是有效的。

        
    15
    2016-07-22 17:33:57Z

    这通常不是最有效的解决方案,但它可以满足我的需求。简单的测试用例如下......

     functions

    循环阵列测试......

     
    var clone = JSON.parse(JSON.stringify(obj));
    

    功能测试......

     
    function clone(obj, clones) {
        // Makes a deep copy of 'obj'. Handles cyclic structures by
        // tracking cloned obj's in the 'clones' parameter. Functions 
        // are included, but not cloned. Functions members are cloned.
        var new_obj,
            already_cloned,
            t = typeof obj,
            i = 0,
            l,
            pair; 
    
        clones = clones || [];
    
        if (obj === null) {
            return obj;
        }
    
        if (t === "object" || t === "function") {
    
            // check to see if we've already cloned obj
            for (i = 0, l = clones.length; i < l; i++) {
                pair = clones[i];
                if (pair[0] === obj) {
                    already_cloned = pair[1];
                    break;
                }
            }
    
            if (already_cloned) {
                return already_cloned; 
            } else {
                if (t === "object") { // create new object
                    new_obj = new obj.constructor();
                } else { // Just use functions as is
                    new_obj = obj;
                }
    
                clones.push([obj, new_obj]); // keep track of objects we've cloned
    
                for (key in obj) { // clone object members
                    if (obj.hasOwnProperty(key)) {
                        new_obj[key] = clone(obj[key], clones);
                    }
                }
            }
        }
        return new_obj || obj;
    }
    
        
    13
    2011-04-03 02:08:03Z

    AngularJS

    如果你正在使用棱角分明,你也可以这样做

     
    a = []
    a.push("b", "c", a)
    aa = clone(a)
    aa === a //=> false
    aa[2] === a //=> false
    aa[2] === a[2] //=> false
    aa[2] === aa //=> true
    
        
    12
    2016-09-14 13:26:15Z

    我不同意此处投票最多的答案。 递归深度克隆 JSON.parse(JSON.stringify(obj))方法快得多

    这是快速参考的功能:

     
    f = new Function
    f.a = a
    ff = clone(f)
    ff === f //=> true
    ff.a === a //=> false
    
        
    11
    2017-06-18 06:34:37Z
    1. 我喜欢这种方法,但它没有正确处理日期;在检查null`
      之后考虑添加像
      var newObject = angular.copy(oldObject);
      
      这样的东西
      2017-08-21 22:53:25Z
    2. 在循环引用上崩溃。
      2018-03-18 05:19:50Z
    3. 在最新稳定的Firefox中,这比Jsben.ch链接中的其他策略长一个数量级或更多。它以错误的方向击败其他人。
      2019-01-14 18:38:35Z
    4. 醇>
     
    function cloneDeep (o) {
      let newO
      let i
    
      if (typeof o !== 'object') return o
    
      if (!o) return o
    
      if (Object.prototype.toString.apply(o) === '[object Array]') {
        newO = []
        for (i = 0; i < o.length; i += 1) {
          newO[i] = cloneDeep(o[i])
        }
        return newO
      }
    
      newO = {}
      for (i in o) {
        if (o.hasOwnProperty(i)) {
          newO[i] = cloneDeep(o[i])
        }
      }
      return newO
    }
    
    11
    2018-01-17 16:27:11Z
    9
    2016-07-22 17:47:24Z

    这是一个可以克隆任何JavaScript对象的综合clone()方法。它几乎处理所有情况:

     parse     
    8
    2016-07-22 17:31:06Z
    1. 它将原语转换为包装器对象,在大多数情况下不是一个好的解决方案。
      2014-08-01 09:58:31Z
    2. @ DanubianSailor - 我认为它没有...它似乎从一开始就返回原语,并且似乎没有对它们做任何事情这会在返回时将它们变成包装对象。
      2016-02-02 18:06:42Z
    3. 醇>
    function clone(obj) {
      var regExp = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
      return JSON.parse(JSON.stringify(x), function(k, v) {
        if (typeof v === 'string' && regExp.test(v))
          return new Date(v);
        return v;
      });
    }
    
来源放置 这里