提问



克隆JavaScript对象的最有效方法是什么?我已经看到obj = eval(uneval(o));被使用了,但那是非标准的,只有Firefox支持。

我已经完成了obj = JSON.parse(JSON.stringify(o));这样的事情,但质疑效率。我还看到了具有各种缺陷的递归复制功能。


我很惊讶没有规范的解决方案。[80]

最佳参考



  注意:这是对另一个答案的回复,而不是对此问题的正确回答。如果您希望快速克隆对象,请在回答此问题时遵循Corban的建议。






我想要注意 jQuery 中的.clone()方法只能克隆DOM元素。为了克隆JavaScript对象,你可以这样做:[82]


// Shallow copy
var newObject = jQuery.extend({}, oldObject);

// Deep copy
var newObject = jQuery.extend(true, {}, oldObject);


更多信息可以在jQuery文档中找到。[83]


我还要注意,深层副本实际上比上面显示的更加智能 - 它能够避免许多陷阱(例如,尝试深度扩展DOM元素)。它经常在jQuery核心中使用插件效果很好。

其它参考1


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


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


JSON.parse(JSON.stringify(obj))


成为深度克隆对象的最快方法(它将jQuery.extend与深度标志设置为10-20%)。[85]


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


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


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


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


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


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


此外,请注意,至少在Chrome 65中,本机克隆是不可取的。根据这个JSPerf,通过创建一个新函数来执行本机克隆比使用JSON.stringify慢几乎<800> ,这一切都非常快。[87]

其它参考2


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


var newObject = JSON.parse(JSON.stringify(oldObject));

其它参考3


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




    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;
    }



结构化克隆



HTML5定义了内部结构化克隆算法,可以创建对象的深层克隆。它仍然局限于某些内置类型,但除了JSON支持的少数类型之外,它还支持日期,RegExps,地图,集合,Blob,FileLists,ImageDatas,稀疏数组,类型数组,以及未来可能更多。它还保留了克隆数据中的引用,允许它支持会导致JSON错误的循环和递归结构。[88] [89]


浏览器的直接支持:即将推出? 🙂



浏览器目前不提供结构化克隆算法的直接接口,但GitHub上的whatwg/html#793正在积极讨论全局structuredClone()函数,可能即将推出!正如目前提出的那样,在大多数情况下使用它将如此简单:[90]


const clone = structuredClone(original);


在此之前,浏览器结构化克隆实现仅间接暴露。


异步解决方法:可用。 😕



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


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();



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



Object.assign方法是ECMAScript 2015(ES6)标准的一部分,完全符合您的需要。[92]


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



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



阅读更多...... [93]


polyfill 支持旧版浏览器:


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;
    }
  });
}

其它参考4


码:


// 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);

其它参考5


这就是我使用的:


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;
}

其它参考6


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



  • 重新分配=(字符串数组,仅数字数组)

  • Slice(字符串数组,仅数字数组)

  • 连接(字符串数组,仅数字数组)

  • 自定义功能:for-loop或递归复制

  • jQuerys $ .extend

  • JSON.parse(字符串数组,数组数组,仅限对象数组)

  • Underscore.js的_.clone(字符串数组,仅数字数组)

  • Lo-Dashs _.cloneDeep



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


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


重新分配的地方表现最快:


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


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


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'}];


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


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);


使用第三方实用程序功能:


$.extend(true, [], arr1); // Jquery Extend
JSON.parse(arr1);
_.cloneDeep(arr1); // Lo-dash


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



  • http://jsperf.com/js-deep-copy/2

  • http://jsperf.com/jquery-extend-vs-json-parse/2


其它参考7


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});

其它参考8


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


只要你没有将对象分配给任何东西,它就不会在内存中保留引用。所以要创建一个你想要在其他对象之间共享的对象,你必须创建一个像这样的工厂:


var a = function(){
    return {
        father:'zacharias'
    };
},
b = a(),
c = a();
c.father = 'johndoe';
alert(b.father);

其它参考9


有一个库(称为克隆),这样做很好。它提供了我所知道的最完整的递归克隆/复制任意对象。它还支持循环引用,但尚未涵盖其他答案。[98]


你也可以在npm找到它。它可以用于浏览器以及Node.js. [99]


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


安装它


npm install clone


或用Ender打包。[100]


ender build clone [...]


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


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


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' } }


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

其它参考10


如果你正在使用它,Underscore.js库有一个克隆方法。[101] [102]


var newObject = _.clone(oldObject);

其它参考11


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


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


复制此对象的方法很少,无需更改原点:


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


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.");
}


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


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


3)AngularJs:


var  deepCopyObj = angular.copy(obj);


4)jQuery:


var deepCopyObj = jQuery.extend(true, {}, obj);


5)UnderscoreJs& Loadash:


var deepCopyObj = _.cloneDeep(obj); //latest version UndescoreJs makes shallow copy


希望这些帮助......

其它参考12


这里是ConroyP上面的答案的一个版本,即使构造函数需要参数,它也能工作:


//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;
}


我的simpleoo库中也提供了这个功能。[103]





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


/**
 * 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();
    };
}

其它参考13


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


var objToCreate = JSON.parse(JSON.stringify(cloneThis));

其它参考14


function clone(obj)
 { var clone = {};
   clone.prototype = obj.prototype;
   for (property in obj) clone[property] = obj[property];
   return clone;
 }

其它参考15


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


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;


newObject.a; // is 5


但:


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

其它参考16


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


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


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


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


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


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

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

其它参考18


仅仅因为我没有看到AngularJS提到并且认为人们可能想知道...... [107]


angular.copy还提供深度复制对象和数组的方法。[108]

其它参考19


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


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)

其它参考20


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


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


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


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


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


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


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


现在,对于非普通的JavaScript对象,并不是一个非常简单的答案。实际上,由于JavaScript函数和内部对象状态的动态特性,实际上并非如此。深入克隆具有内部函数的JSON结构需要重新创建这些函数及其内部上下文。 JavaScript只是没有标准化的方法来做到这一点。


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


我们写了自己的,但我所看到的最好的一般方法在这里介绍:


http://davidwalsh.name/javascript-clone[109]


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


主要思想是你需要特殊地处理你的函数(或原型类,即可以说)的实例化。在这里,他提供了几个RegExp和Date的例子。


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


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


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

其它参考21


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


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;
}


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


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


功能测试...


f = new Function
f.a = a
ff = clone(f)
ff === f //=> true
ff.a === a //=> false

其它参考22


AngularJS



好吧,如果你使用角度,你也可以做到这一点


var newObject = angular.copy(oldObject);

其它参考23


我在这里以最大的选票不同意答案。 递归深度克隆比 JSON.parse(JSON.stringify(obj))方法快得多



  • Jsperf 在这里排名第一:https://jsperf.com/deep-copy-vs-json-stringify-json-parse/5

  • Jsben 从上面的答案中更新,以显示递归深度克隆击败所有其他提到的:http://jsben.ch/13YKQ



这里有快速参考的功能:[111] [112]


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
}

其它参考24


// obj target object, vals source object
var setVals = function (obj, vals) {
    if (obj && vals) {
        for (var x in vals) {
            if (vals.hasOwnProperty(x)) {
                if (obj[x] && typeof vals[x] === 'object') {
                    obj[x] = setVals(obj[x], vals[x]);
                } else {
                    obj[x] = vals[x];
                }
            }
        }
    }
    return obj;
};

其它参考25


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


function clone(src, deep) {

    var toString = Object.prototype.toString;
    if (!src && typeof src != "object") {
        // Any non-object (Boolean, String, Number), null, undefined, NaN
        return src;
    }

    // Honor native/custom clone methods
    if (src.clone && toString.call(src.clone) == "[object Function]") {
        return src.clone(deep);
    }

    // DOM elements
    if (src.nodeType && toString.call(src.cloneNode) == "[object Function]") {
        return src.cloneNode(deep);
    }

    // Date
    if (toString.call(src) == "[object Date]") {
        return new Date(src.getTime());
    }

    // RegExp
    if (toString.call(src) == "[object RegExp]") {
        return new RegExp(src);
    }

    // Function
    if (toString.call(src) == "[object Function]") {

        //Wrap in another method to make sure == is not true;
        //Note: Huge performance issue due to closures, comment this :)
        return (function(){
            src.apply(this, arguments);
        });
    }

    var ret, index;
    //Array
    if (toString.call(src) == "[object Array]") {
        //[].slice(0) would soft clone
        ret = src.slice();
        if (deep) {
            index = ret.length;
            while (index--) {
                ret[index] = clone(ret[index], true);
            }
        }
    }
    //Object
    else {
        ret = src.constructor ? new src.constructor() : {};
        for (var prop in src) {
            ret[prop] = deep
                ? clone(src[prop], true)
                : src[prop];
        }
    }
    return ret;
};

其它参考26


我通常使用var newObj = JSON.parse( JSON.stringify(oldObje) );但是,这是一个更正确的方法:


var o = {};

var oo = Object.create(o);

(o === oo); // => false


观看旧版浏览器!

其它参考27


只有当你可以使用ECMAScript 6或者转发器时。[113] [114]


特征:



  • 在复制时不会触发getter/setter。

  • 保留getter/setter。

  • 保留原型信息。

  • 适用于 object-literal 功能 OO书写样式。



代码:[115]


function clone(target, source){

    for(let key in source){

        // Use getOwnPropertyDescriptor instead of source[key] to prevent from trigering setter/getter.
        let descriptor = Object.getOwnPropertyDescriptor(source, key);
        if(descriptor.value instanceof String){
            target[key] = new String(descriptor.value);
        }
        else if(descriptor.value instanceof Array){
            target[key] = clone([], descriptor.value);
        }
        else if(descriptor.value instanceof Object){
            let prototype = Reflect.getPrototypeOf(descriptor.value);
            let cloneObject = clone({}, descriptor.value);
            Reflect.setPrototypeOf(cloneObject, prototype);
            target[key] = cloneObject;
        }
        else {
            Object.defineProperty(target, key, descriptor);
        }
    }
    let prototype = Reflect.getPrototypeOf(source);
    Reflect.setPrototypeOf(target, prototype);
    return target;
}