我有一个对象, x. 。我想将其复制为对象 y, ,这样就变成了 y 请勿修改 x. 。我意识到复制从内置 JavaScript 对象派生的对象会产生额外的、不需要的属性。这不是问题,因为我正在复制我自己的文字构造的对象之一。

如何正确克隆 JavaScript 对象?

有帮助吗?

解决方案

要用于在JavaScript中的任何对象做这不会是简单的或直接的。你会遇到的错误拿起从对象的原型属性应该在原型留下,而不是复制到新实例的问题。如果,例如,您要添加clone方法Object.prototype,因为一些答案描绘,则需要明确地跳过属性。但如果有其他额外的方法添加什么Object.prototype,或其他中间的原型,你不知道?在这种情况下,你会复制你不应该的属性,所以你需要检测与的 hasOwnProperty 方法

在除了不可枚举的属性,您会在尝试复制具有隐藏属性的对象遇到强硬的问题。例如,prototype是一个函数的隐藏属性。此外,对象的原型与属性__proto__,其也被隐藏引用,并且将不会被复制一个for / in循环遍历源对象的属性。我想__proto__可能是专门针对Firefox的JavaScript解释器,它可能是一些在其他浏览器不同,但你得到的图片。并非一切都是枚举。如果你知道它的名字,您可以复制一个隐藏属性,但我不知道有什么方法能够自动发现它。

然而,在寻求一种优雅的解决方案的另一个困难是正确地建立原型继承的问题。如果源对象的原型Object,然后简单地创建与{}一个新的通用对象的工作,但如果源的原型是Object的一些后代,那么你将要丢失从原型您使用hasOwnProperty跳过了其他成员过滤器,或者是在原型的,但不是首先枚举。一种解决方案可能是调用源对象的constructor属性来获取初始复制对象,然后拷贝过来的属性,但你仍然不会得到不可枚举的属性。例如, Date 对象存储其数据作为隐藏构件:

function clone(obj) {
    if (null == obj || "object" != typeof obj) return obj;
    var copy = obj.constructor();
    for (var attr in obj) {
        if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
    }
    return copy;
}

var d1 = new Date();

/* Executes function after 5 seconds. */
setTimeout(function(){
    var d2 = clone(d1);
    alert("d1 = " + d1.toString() + "\nd2 = " + d2.toString());
}, 5000);

d1的日期字符串将是后面的是d2的5秒。一种方法,使一个Date另一个相同是通过调用setTime方法,但这是特定于Date类。我不认为有这个问题防弹通用的解决方案,但我会很乐意错!

当我不得不实施一般深度复制我最终通过假设我只需要复制一个普通ObjectArrayDateStringNumber,或Boolean损害。最后的3种类型是不可变的,这样我就可以执行浅拷贝,而不是担心改变。我还假设包含在ObjectArray任何元件也将是6种简单类型在该列表中的一个。这可以用如下面的代码来实现:

function clone(obj) {
    var copy;

    // Handle the 3 simple types, and null or undefined
    if (null == obj || "object" != typeof obj) return obj;

    // Handle Date
    if (obj instanceof Date) {
        copy = new Date();
        copy.setTime(obj.getTime());
        return copy;
    }

    // Handle Array
    if (obj instanceof Array) {
        copy = [];
        for (var i = 0, len = obj.length; i < len; i++) {
            copy[i] = clone(obj[i]);
        }
        return copy;
    }

    // Handle Object
    if (obj instanceof Object) {
        copy = {};
        for (var attr in obj) {
            if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
        }
        return copy;
    }

    throw new Error("Unable to copy obj! Its type isn't supported.");
}

上述功能将充分地工作为6种简单类型我所提到的,只要在对象和数组数据形成一个树结构。也就是说,有不超过一个参考对象中的相同的数据。例如:

// This would be cloneable:
var tree = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "right" : null,
    "data"  : 8
};

// This would kind-of work, but you would get 2 copies of the 
// inner node instead of 2 references to the same copy
var directedAcylicGraph = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "data"  : 8
};
directedAcyclicGraph["right"] = directedAcyclicGraph["left"];

// Cloning this would cause a stack overflow due to infinite recursion:
var cyclicGraph = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "data"  : 8
};
cyclicGraph["right"] = cyclicGraph;

这不会是能够处理任何JavaScript对象,但它可能是足够的许多purposes只要你不要以为它只是为任何你扔在它的工作。

其他提示

如果你没有你的对象中使用Dates,功能,未定义或无限,很简单的一个内胆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'
}
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()

这适用于所有种类的包含对象,数组,字符串,布尔值和数字对象。

请参阅也是本文关于结构化克隆算法 的浏览器被发布消息,并从一个工人时使用。它也包含用于深克隆的功能。

使用jQuery的,可以的浅复制使用延伸

var copiedObject = jQuery.extend({}, originalObject)

之后的copiedObject变化不会影响originalObject,反之亦然。

或者使一个的深层副本

var copiedObject = jQuery.extend(true, {}, originalObject)

在的ECMAScript 6有 Object.assign 方法,它从一个对象到另一所有可枚举自己的属性的副本的值。例如:

var x = {myProp: "value"};
var y = Object.assign({}, x); 

但要知道,嵌套对象仍然被复制作为参考。

MDN:

  • 如果您想要浅复制,请使用 Object.assign({}, a)
  • 对于“深度”复制,请使用 JSON.parse(JSON.stringify(a))

不需要外部库,但你需要检查 浏览器兼容性优先.

有很多答案,但没有提到 对象.创建 来自 ECMAScript 5,诚然,它不会为您提供精确的副本,但会将源设置为新对象的原型。

因此,这不是问题的准确答案,但它是一个单行解决方案,因此很优雅。它最适合两种情况:

  1. 这种继承在哪里有用(废话!)
  2. 源对象不会被修改,因此这两个对象之间的关系不再是问题。

例子:

var foo = { a : 1 };
var bar = Object.create(foo);
foo.a; // 1
bar.a; // 1
foo.a = 2;
bar.a; // 2 - prototype changed
bar.a = 3;
foo.a; // Still 2, since setting bar.a makes it an "own" property

为什么我认为这个解决方案更优越?它是原生的,因此没有循环,没有递归。然而,较旧的浏览器将需要一个polyfill。

一种优雅的方式在一个代码行克隆Javascript对象

这是Object.assign方法是ECMAScript的2015(ES6)的一部分标准和确实需要什么。

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

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

更多信息

填充工具以支持旧的浏览器:

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

互联网上的大多数解决方案都存在几个问题。所以我决定进行跟进,其中包括为什么不应该接受已接受的答案。

起始情况

我想要 深拷贝 一个 JavaScript Object 及其所有的孩子和他们的孩子等等。但由于我不是一个普通的开发人员,我的 Object普通的 properties, circular structures 乃至 nested objects.

所以让我们创建一个 circular structure 和一个 nested object 第一的。

function Circ() {
    this.me = this;
}

function Nested(y) {
    this.y = y;
}

让我们将所有内容整合到一起 Object 命名的 a.

var a = {
    x: 'a',
    circ: new Circ(),
    nested: new Nested('a')
};

接下来,我们要复制 a 到一个名为 b 并改变它。

var b = a;

b.x = 'b';
b.nested.y = 'b';

你知道这里发生了什么,因为如果不知道,你甚至不会回答这个伟大的问题。

console.log(a, b);

a --> Object {
    x: "b",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

b --> Object {
    x: "b",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

现在我们来寻找解决方案。

JSON

我尝试的第一次尝试是使用 JSON.

var b = JSON.parse( JSON.stringify( a ) );

b.x = 'b';
b.nested.y = 'b';

不要在上面浪费太多时间,你会得到 TypeError: Converting circular structure to JSON.

递归复制 (接受的“答案”)

让我们看一下已接受的答案。

function cloneSO(obj) {
    // Handle the 3 simple types, and null or undefined
    if (null == obj || "object" != typeof obj) return obj;

    // Handle Date
    if (obj instanceof Date) {
        var copy = new Date();
        copy.setTime(obj.getTime());
        return copy;
    }

    // Handle Array
    if (obj instanceof Array) {
        var copy = [];
        for (var i = 0, len = obj.length; i < len; i++) {
            copy[i] = cloneSO(obj[i]);
        }
        return copy;
    }

    // Handle Object
    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! Its type isn't supported.");
}

看起来不错吧?它是对象的递归副本,也可以处理其他类型,例如 Date, ,但这不是一个要求。

var b = cloneSO(a);

b.x = 'b';
b.nested.y = 'b';

递归和 circular structures 不能很好地协同工作... RangeError: Maximum call stack size exceeded

本机解决方案

和同事吵架后,老板问我们发生了什么事,他找到了一个简单的办法 解决方案 经过一番谷歌搜索后。它被称为 Object.create.

var b = Object.create(a);

b.x = 'b';
b.nested.y = 'b';

这个解决方案前段时间被添加到Javascript中,甚至可以处理 circular structure.

console.log(a, b);

a --> Object {
    x: "a",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

b --> Object {
    x: "b",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

...你看,它不适用于内部的嵌套结构。

原生解决方案的polyfill

有一个polyfill用于 Object.create 在较旧的浏览器中,例如 IE 8。这类似于 Mozilla 推荐的东西,当然,它并不完美,并且会导致与 本机解决方案.

function F() {};
function clonePF(o) {
    F.prototype = o;
    return new F();
}

var b = clonePF(a);

b.x = 'b';
b.nested.y = 'b';

我已经把 F 超出了范围,所以我们可以看看是什么 instanceof 告诉我们。

console.log(a, b);

a --> Object {
    x: "a",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

b --> F {
    x: "b",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

console.log(typeof a, typeof b);

a --> object
b --> object

console.log(a instanceof Object, b instanceof Object);

a --> true
b --> true

console.log(a instanceof F, b instanceof F);

a --> false
b --> true

与以下问题相同 本机解决方案, ,但是输出稍差一些。

更好(但不完美)的解决方案

在挖掘时,我发现了类似的问题(在Javascript中,当执行深复制时,由于属性是“this”,如何避免循环?)对此,但有一种更好的解决方案。

function cloneDR(o) {
    const gdcc = "__getDeepCircularCopy__";
    if (o !== Object(o)) {
        return o; // primitive value
    }

    var set = gdcc in o,
        cache = o[gdcc],
        result;
    if (set && typeof cache == "function") {
        return cache();
    }
    // else
    o[gdcc] = function() { return result; }; // overwrite
    if (o instanceof Array) {
        result = [];
        for (var i=0; i<o.length; i++) {
            result[i] = cloneDR(o[i]);
        }
    } else {
        result = {};
        for (var prop in o)
            if (prop != gdcc)
                result[prop] = cloneDR(o[prop]);
            else if (set)
                result[prop] = cloneDR(cache);
    }
    if (set) {
        o[gdcc] = cache; // reset
    } else {
        delete o[gdcc]; // unset again
    }
    return result;
}

var b = cloneDR(a);

b.x = 'b';
b.nested.y = 'b';

让我们看一下输出......

console.log(a, b);

a --> Object {
    x: "a",
    circ: Object {
        me: Object { ... }
    },
    nested: Object {
        y: "a"
    }
}

b --> Object {
    x: "b",
    circ: Object {
        me: Object { ... }
    },
    nested: Object {
        y: "b"
    }
}

console.log(typeof a, typeof b);

a --> object
b --> object

console.log(a instanceof Object, b instanceof Object);

a --> true
b --> true

console.log(a instanceof F, b instanceof F);

a --> false
b --> false

要求是匹配的,但仍然存在一些较小的问题,包括更改 instancenestedcircObject.

共享一片叶子的树的结构不会被复制,它们将成为两个独立的叶子:

        [Object]                     [Object]
         /    \                       /    \
        /      \                     /      \
      |/_      _\|                 |/_      _\|  
  [Object]    [Object]   ===>  [Object]    [Object]
       \        /                 |           |
        \      /                  |           |
        _\|  |/_                 \|/         \|/
        [Object]               [Object]    [Object]

结论

使用递归和缓存的最后一个解决方案可能不是最好的,但它是一个 真实的 对象的深拷贝。它处理简单 properties, circular structuresnested object, ,但克隆时会弄乱它们的实例。

jsfiddle

如果你没事带着浅浅的复印件,underscore.js库有克隆方法。

y = _.clone(x);

,也可以像

扩展它
copiedObject = _.extend({},originalObject);

确定想象你有低于这个目标,你想克隆它:

let obj = {a:1, b:2, c:3}; //ES6

var obj = {a:1, b:2, c:3}; //ES5

答案主要depeneds上的的EcmaScript 您使用,在ES6+,你可以简单地使用Object.assign做克隆:

let cloned = Object.assign({}, obj); //new {a:1, b:2, c:3};

或使用传播算子是这样的:

let cloned = {...obj}; //new {a:1, b:2, c:3};

但是,如果你使用ES5,你可以使用一些方法,但JSON.stringify,只要确保你不使用数据复制的一大块,但它可能是在许多情况下,一行方便的方法,这样的事情:

let cloned = JSON.parse(JSON.stringify(obj)); 
//new {a:1, b:2, c:3};, can be handy, but avoid using on big chunk of data over and over

一个特别不雅解决方案是使用JSON编码,使不具有构件方法的对象的深层副本。该方法是JSON编码您的目标对象,然后通过解码它,你就会得到你正在寻找的副本。你可以只要你想,你需要让尽可能多的拷贝多次进行解码。

当然,功能在JSON属于,所以这仅适用于对象,而不构件的方法。

这个方法很适合我的使用情况,因为我存储在key-value存储JSON斑点,而当它们暴露为一个JavaScript API中的对象,每个对象实际上包含的原始状态的副本对象,以便我们可以计算出增量呼叫者已经变异暴露对象之后。

var object1 = {key:"value"};
var object2 = object1;

object2 = JSON.stringify(object1);
object2 = JSON.parse(object2);

object2.key = "a change";
console.log(object1);// returns value

您可以简单地使用传播特性以复制对象,而不引用。但要小心(见注释),“复制”只是上最低的对象/阵列级别。嵌套属性仍引用!


<强>完整克隆:

let x = {a: 'value1'}
let x2 = {...x}

// => mutate without references:

x2.a = 'value2'
console.log(x.a)    // => 'value1'

<强>克隆与第二级的引用:

const y = {a: {b: 'value3'}}
const y2 = {...y}

// => nested object is still a references:

y2.a.b = 'value4'
console.log(y.a.b)    // => 'value4'

的JavaScript实际上不支持深克隆本身。使用的效用函数。例如Ramda:

  

http://ramdajs.com/docs/#clone

有关使用AngularJS那些,还存在用于克隆或在该库中的对象的延伸直接的方法。

var destination = angular.copy(source);

angular.copy(source, destination);

更多angular.copy 文档中 ...

A.Levy的答案是几乎完全,这是我的一点贡献:有一种方法如何处理递归引用,看到这一行

if(this[attr]==this) copy[attr] = copy;

如果该对象是XML DOM元素,必须使用 cloneNode 而不是

if(this.cloneNode) return this.cloneNode(true);

通过A.Levy的详尽的研究和Calvin的原型方法的启发

,我提供这样的解决方案:

Object.prototype.clone = function() {
  if(this.cloneNode) return this.cloneNode(true);
  var copy = this instanceof Array ? [] : {};
  for(var attr in this) {
    if(typeof this[attr] == "function" || this[attr]==null || !this[attr].clone)
      copy[attr] = this[attr];
    else if(this[attr]==this) copy[attr] = copy;
    else copy[attr] = this[attr].clone();
  }
  return copy;
}

Date.prototype.clone = function() {
  var copy = new Date();
  copy.setTime(this.getTime());
  return copy;
}

Number.prototype.clone = 
Boolean.prototype.clone =
String.prototype.clone = function() {
  return this;
}

又见安迪·伯克的笔记中的答案。

下面是可以使用的功能。

function clone(obj) {
    if(obj == null || typeof(obj) != 'object')
        return obj;    
    var temp = new obj.constructor(); 
    for(var key in obj)
        temp[key] = clone(obj[key]);    
    return temp;
}

从这篇文章:如何复制数组和对象在Javascript 由Brian Huisman的:

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

在ES-6可以简单地使用Object.assign(...)。 例如:

let obj = {person: 'Thor Odinson'};
let clone = Object.assign({}, obj);

一个很好的参考是在这里: https://googlechrome.github.io/samples/object-assign-es6/

在的ECMAScript 2018

let objClone = { ...obj };

注意的嵌套对象仍然复制的作为参考。

可以克隆对象并删除从使用一个单一的代码行中的前一个的任何参考。简单地做:

var obj1 = { text: 'moo1' };
var obj2 = Object.create(obj1); // Creates a new clone without references

obj2.text = 'moo2'; // Only updates obj2's text property

console.log(obj1, obj2); // Outputs: obj1: {text:'moo1'}, obj2: {text:'moo2'}

有关浏览器/引擎目前不支持的Object.create您可以使用此填充工具:

// Polyfill Object.create if it does not exist
if (!Object.create) {
    Object.create = function (o) {
        var F = function () {};
        F.prototype = o;
        return new F();
    };
}

老问题的新答案!如果您有幸使用 ECMAScript 2016 (ES6) 传播语法, , 这很容易。

keepMeTheSame = {first: "Me!", second: "You!"};
cloned = {...keepMeTheSame}

这为对象的浅拷贝提供了一种干净的方法。进行深层复制,意味着为每个递归嵌套对象中的每个值创建一个新副本,需要上述较重的解决方案。

JavaScript 不断发展。

let clone = Object.assign( Object.create( Object.getPrototypeOf(obj)), obj)

ES6解决方案,如果你想(浅)克隆类实例并不仅仅是一个属性对象。

对克隆简单对象感兴趣:

JSON.parse(JSON.stringify(json_original));

来源 : 如何不通过引用将 JavaScript 对象复制到新变量?

使用Lodash:

var y = _.clone(x, true);

我认为有一个简单且有效的答案。在深度复制中有两个问题:

  1. 保持属性相互独立。
  2. 并使克隆对象上的方法保持活动状态。

所以我认为一个简单的解决方案是首先序列化和反序列化,然后对其进行分配以复制函数。

let deepCloned = JSON.parse(JSON.stringify(source));
let merged = Object.assign({}, source);
Object.assign(merged, deepCloned);

虽然这个问题有很多答案,但我希望这个也能有所帮助。

有关深层副本和克隆,然后JSON.stringify JSON.parse对象:

obj = { a: 0 , b: { c: 0}};
let deepClone = JSON.parse(JSON.stringify(obj));
obj.a = 5;
obj.b.c = 5;
console.log(JSON.stringify(deepClone)); // { a: 0, b: { c: 0}}

我只是想添加到所有的解决方案Object.create在这个岗位,这不符合的的NodeJS所期望的方式工作。

在火狐的结果

var a = {"test":"test"};
var b = Object.create(a);
console.log(b);´

{test:"test"}

在它的NodeJS是

{}

这是A. Levy的代码也处理的功能和多个/循环引用克隆的适应 - 这意味着什么是,如果在树中这两种性能克隆是同一个对象的引用,克隆的对象树将这些属性指向一个与引用对象的同一克隆。这也解决了循环依赖的情况下,如果离开未处理,导致一个无限循环。所述算法的复杂度是O(n)

function clone(obj){
    var clonedObjectsArray = [];
    var originalObjectsArray = []; //used to remove the unique ids when finished
    var next_objid = 0;

    function objectId(obj) {
        if (obj == null) return null;
        if (obj.__obj_id == undefined){
            obj.__obj_id = next_objid++;
            originalObjectsArray[obj.__obj_id] = obj;
        }
        return obj.__obj_id;
    }

    function cloneRecursive(obj) {
        if (null == obj || typeof obj == "string" || typeof obj == "number" || typeof obj == "boolean") return obj;

        // Handle Date
        if (obj instanceof Date) {
            var copy = new Date();
            copy.setTime(obj.getTime());
            return copy;
        }

        // Handle Array
        if (obj instanceof Array) {
            var copy = [];
            for (var i = 0; i < obj.length; ++i) {
                copy[i] = cloneRecursive(obj[i]);
            }
            return copy;
        }

        // Handle Object
        if (obj instanceof Object) {
            if (clonedObjectsArray[objectId(obj)] != undefined)
                return clonedObjectsArray[objectId(obj)];

            var copy;
            if (obj instanceof Function)//Handle Function
                copy = function(){return obj.apply(this, arguments);};
            else
                copy = {};

            clonedObjectsArray[objectId(obj)] = copy;

            for (var attr in obj)
                if (attr != "__obj_id" && obj.hasOwnProperty(attr))
                    copy[attr] = cloneRecursive(obj[attr]);                 

            return copy;
        }       


        throw new Error("Unable to copy obj! Its type isn't supported.");
    }
    var cloneObj = cloneRecursive(obj);



    //remove the unique ids
    for (var i = 0; i < originalObjectsArray.length; i++)
    {
        delete originalObjectsArray[i].__obj_id;
    };

    return cloneObj;
}

一些快速测试

var auxobj = {
    prop1 : "prop1 aux val", 
    prop2 : ["prop2 item1", "prop2 item2"]
    };

var obj = new Object();
obj.prop1 = "prop1_value";
obj.prop2 = [auxobj, auxobj, "some extra val", undefined];
obj.nr = 3465;
obj.bool = true;

obj.f1 = function (){
    this.prop1 = "prop1 val changed by f1";
};

objclone = clone(obj);

//some tests i've made
console.log("test number, boolean and string cloning: " + (objclone.prop1 == obj.prop1 && objclone.nr == obj.nr && objclone.bool == obj.bool));

objclone.f1();
console.log("test function cloning 1: " + (objclone.prop1 == 'prop1 val changed by f1'));
objclone.f1.prop = 'some prop';
console.log("test function cloning 2: " + (obj.f1.prop == undefined));

objclone.prop2[0].prop1 = "prop1 aux val NEW";
console.log("test multiple references cloning 1: " + (objclone.prop2[1].prop1 == objclone.prop2[0].prop1));
console.log("test multiple references cloning 2: " + (objclone.prop2[1].prop1 != obj.prop2[0].prop1));
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;
};

我写我自己的实现。不知道这是否算作一个更好的解决方案:

/*
    a function for deep cloning objects that contains other nested objects and circular structures.
    objects are stored in a 3D array, according to their length (number of properties) and their depth in the original object.
                                    index (z)
                                         |
                                         |
                                         |
                                         |
                                         |
                                         |                      depth (x)
                                         |_ _ _ _ _ _ _ _ _ _ _ _
                                        /_/_/_/_/_/_/_/_/_/
                                       /_/_/_/_/_/_/_/_/_/
                                      /_/_/_/_/_/_/...../
                                     /................./
                                    /.....            /
                                   /                 /
                                  /------------------
            object length (y)    /
*/

以下是实现:

function deepClone(obj) {
    var depth = -1;
    var arr = [];
    return clone(obj, arr, depth);
}

/**
 *
 * @param obj source object
 * @param arr 3D array to store the references to objects
 * @param depth depth of the current object relative to the passed 'obj'
 * @returns {*}
 */
function clone(obj, arr, depth){
    if (typeof obj !== "object") {
        return obj;
    }

    var length = Object.keys(obj).length; // native method to get the number of properties in 'obj'

    var result = Object.create(Object.getPrototypeOf(obj)); // inherit the prototype of the original object
    if(result instanceof Array){
        result.length = length;
    }

    depth++; // depth is increased because we entered an object here

    arr[depth] = []; // this is the x-axis, each index here is the depth
    arr[depth][length] = []; // this is the y-axis, each index is the length of the object (aka number of props)
    // start the depth at current and go down, cyclic structures won't form on depths more than the current one
    for(var x = depth; x >= 0; x--){
        // loop only if the array at this depth and length already have elements
        if(arr[x][length]){
            for(var index = 0; index < arr[x][length].length; index++){
                if(obj === arr[x][length][index]){
                    return obj;
                }
            }
        }
    }

    arr[depth][length].push(obj); // store the object in the array at the current depth and length
    for (var prop in obj) {
        if (obj.hasOwnProperty(prop)) result[prop] = clone(obj[prop], arr, depth);
    }

    return result;
}

以上扬Turoň的回答很接近,而且可能是最好的浏览器来使用,由于兼容性问题,但它会潜在地导致一些奇怪的枚举的问题。例如,执行:

for ( var i in someArray ) { ... }

将通过阵列的元件迭代之后分配clone()方法步骤i。下面是避免枚举和使用的是Node.js作品改编:

Object.defineProperty( Object.prototype, "clone", {
    value: function() {
        if ( this.cloneNode )
        {
            return this.cloneNode( true );
        }

        var copy = this instanceof Array ? [] : {};
        for( var attr in this )
        {
            if ( typeof this[ attr ] == "function" || this[ attr ] == null || !this[ attr ].clone )
            {
                copy[ attr ] = this[ attr ];
            }
            else if ( this[ attr ] == this )
            {
                copy[ attr ] = copy;
            }
            else
            {
                copy[ attr ] = this[ attr ].clone();
            }
        }
        return copy;
    }
});

Object.defineProperty( Date.prototype, "clone", {
    value: function() {
        var copy = new Date();
        copy.setTime( this.getTime() );
        return copy;
    }
});

Object.defineProperty( Number.prototype, "clone", { value: function() { return this; } } );
Object.defineProperty( Boolean.prototype, "clone", { value: function() { return this; } } );
Object.defineProperty( String.prototype, "clone", { value: function() { return this; } } );

这避免了使clone()方法可枚举因为defineProperty()默认枚举为false。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top