简体   繁体   English

如何正确克隆 JavaScript object?

[英]How do I correctly clone a JavaScript object?

I have an object x .我有一个 object x I'd like to copy it as object y , such that changes to y do not modify x .我想将其复制为 object y ,这样对y的更改不会修改x I realized that copying objects derived from built-in JavaScript objects will result in extra, unwanted properties.我意识到复制从内置 JavaScript 对象派生的对象将导致额外的不需要的属性。 This isn't a problem, since I'm copying one of my own literal-constructed objects.这不是问题,因为我正在复制我自己的文字构造对象之一。

How do I correctly clone a JavaScript object?如何正确克隆 JavaScript object?

2022 update 2022 年更新

There's a new JS standard called structured cloning .有一个称为结构化克隆的新 JS 标准。 It works on all browsers:它适用于所有浏览器:

const clone = structuredClone(object);

Old answer旧答案

To do this for any object in JavaScript will not be simple or straightforward.对 JavaScript 中的任何对象执行此操作都不会简单或直接。 You will run into the problem of erroneously picking up attributes from the object's prototype that should be left in the prototype and not copied to the new instance.您将遇到错误地从对象原型中获取属性的问题,这些属性应该留在原型中而不是复制到新实例中。 If, for instance, you are adding a clone method to Object.prototype , as some answers depict, you will need to explicitly skip that attribute.例如,如果您要向Object.prototype添加一个clone方法,正如一些答案所描述的那样,您将需要显式跳过该属性。 But what if there are other additional methods added to Object.prototype , or other intermediate prototypes, that you don't know about?但是,如果在Object.prototype或其他中间原型中添加了您不知道的其他附加方法怎么办? In that case, you will copy attributes you shouldn't, so you need to detect unforeseen, non-local attributes with the hasOwnProperty method.在这种情况下,您将复制不应该复制的属性,因此您需要使用hasOwnProperty方法检测不可预见的非本地属性。

In addition to non-enumerable attributes, you'll encounter a tougher problem when you try to copy objects that have hidden properties.除了不可枚举的属性之外,当您尝试复制具有隐藏属性的对象时,您还会遇到更棘手的问题。 For example, prototype is a hidden property of a function.例如, prototype是函数的隐藏属性。 Also, an object's prototype is referenced with the attribute __proto__ , which is also hidden, and will not be copied by a for/in loop iterating over the source object's attributes.此外,对象的原型由属性__proto__引用,该属性也是隐藏的,并且不会被迭代源对象属性的 for/in 循环复制。 I think __proto__ might be specific to Firefox's JavaScript interpreter and it may be something different in other browsers, but you get the picture.我认为__proto__可能特定于 Firefox 的 JavaScript 解释器,并且在其他浏览器中可能有所不同,但你明白了。 Not everything is enumerable.并非所有事物都是可枚举的。 You can copy a hidden attribute if you know its name, but I don't know of any way to discover it automatically.如果您知道它的名称,您可以复制隐藏的属性,但我不知道有什么方法可以自动发现它。

Yet another snag in the quest for an elegant solution is the problem of setting up the prototype inheritance correctly.寻求优雅解决方案的另一个障碍是正确设置原型继承的问题。 If your source object's prototype is Object , then simply creating a new general object with {} will work, but if the source's prototype is some descendant of Object , then you are going to be missing the additional members from that prototype which you skipped using the hasOwnProperty filter, or which were in the prototype, but weren't enumerable in the first place.如果您的源对象的原型是Object ,那么只需使用{}创建一个新的通用对象就可以了,但如果源的原型是Object的某个后代,那么您将丢失该原型中使用跳过的其他成员hasOwnProperty过滤器,或者在原型中,但一开始就无法枚举。 One solution might be to call the source object's constructor property to get the initial copy object and then copy over the attributes, but then you still will not get non-enumerable attributes.一种解决方案可能是调用源对象的constructor属性来获取初始复制对象,然后复制属性,但是您仍然不会获得不可枚举的属性。 For example, a Date object stores its data as a hidden member:例如,一个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);

The date string for d1 will be 5 seconds behind that of d2 . d1的日期字符串将比d2晚 5 秒。 A way to make one Date the same as another is by calling the setTime method, but that is specific to the Date class.使一个Date与另一个 Date 相同的一种方法是调用setTime方法,但这是特定于Date类的。 I don't think there is a bullet-proof general solution to this problem, though I would be happy to be wrong!我认为这个问题没有万无一失的通用解决方案,尽管我很乐意犯错!

When I had to implement general deep copying I ended up compromising by assuming that I would only need to copy a plain Object , Array , Date , String , Number , or Boolean .当我不得不实现一般的深度复制时,我最终妥协了,假设我只需要复制一个普通的ObjectArrayDateStringNumberBoolean The last 3 types are immutable, so I could perform a shallow copy and not worry about it changing.最后 3 种类型是不可变的,所以我可以执行浅拷贝而不用担心它会改变。 I further assumed that any elements contained in Object or Array would also be one of the 6 simple types in that list.我进一步假设ObjectArray中包含的任何元素也将是该列表中的 6 种简单类型之一。 This can be accomplished with code like the following:这可以通过如下代码来完成:

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

The above function will work adequately for the 6 simple types I mentioned, as long as the data in the objects and arrays form a tree structure.只要对象和数组中的数据形成树形结构,上述函数就可以充分适用于我提到的 6 种简单类型。 That is, there isn't more than one reference to the same data in the object.也就是说,对象中对相同数据的引用不超过一个。 For example:例如:

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

It will not be able to handle any JavaScript object, but it may be sufficient for many purposes as long as you don't assume that it will just work for anything you throw at it.它不能处理任何 JavaScript 对象,但它可能足以满足许多目的,只要你不认为它只适用于你扔给它的任何东西。

If you do not use Date s, functions, undefined, regExp or Infinity within your object, a very simple one liner is JSON.parse(JSON.stringify(object)) :如果您不在对象中使用Date s、函数、未定义、regExp 或 Infinity,则一个非常简单的衬里是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()

This works for all kind of objects containing objects, arrays, strings, booleans and numbers.这适用于包含对象、数组、字符串、布尔值和数字的所有类型的对象。

See also this article about the structured clone algorithm of browsers which is used when posting messages to and from a worker.另请参阅这篇关于浏览器的结构化克隆算法的文章,该算法在向工作人员发送消息和从工作人员发送消息时使用。 It also contains a function for deep cloning.它还包含深度克隆功能。

With jQuery, you can shallow copy with extend :使用 jQuery,您可以使用extend进行浅拷贝

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

subsequent changes to the copiedObject will not affect the originalObject , and vice versa.copiedObject的后续更改不会影响originalObject ,反之亦然。

Or to make a deep copy :或者制作一个深拷贝

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

In ECMAScript 6 there is Object.assign method, which copies values of all enumerable own properties from one object to another.在 ECMAScript 6 中有Object.assign方法,它将所有可枚举自身属性的值从一个对象复制到另一个对象。 For example:例如:

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

But be aware this is a shallow copy - nested objects are still copied as reference.但请注意,这是一个浅拷贝- 嵌套对象仍被复制为引用。

Per MDN :每个MDN

  • If you want shallow copy, use Object.assign({}, a)如果你想要浅拷贝,使用Object.assign({}, a)
  • For "deep" copy, use JSON.parse(JSON.stringify(a))对于“深度”复制,使用JSON.parse(JSON.stringify(a))

There is no need for external libraries but you need to check browser compatibility first .不需要外部库,但您需要先检查浏览器兼容性

An elegant way to clone a Javascript object in one line of code一种在一行代码中克隆 Javascript 对象的优雅方法

An Object.assign method is part of the ECMAScript 2015 (ES6) standard and does exactly what you need. Object.assign方法是 ECMAScript 2015 (ES6) 标准的一部分,可以满足您的需求。

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

The Object.assign() method is used to copy the values of all enumerable own properties from one or more source objects to a target object. Object.assign() 方法用于将所有可枚举自身属性的值从一个或多个源对象复制到目标对象。

Read more... 阅读更多...

The polyfill to support older browsers:支持旧浏览器的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;
    }
  });
}

There are many answers, but none that mentions Object.create from ECMAScript 5, which admittedly does not give you an exact copy, but sets the source as the prototype of the new object.有很多答案,但没有一个提到 ECMAScript 5 中的Object.create ,它诚然没有给你一个精确的副本,而是将源设置为新对象的原型。

Thus, this is not an exact answer to the question, but it is a one-line solution and thus elegant.因此,这不是问题的确切答案,但它是一种单线解决方案,因此很优雅。 And it works best for 2 cases:它最适合两种情况:

  1. Where such inheritance is useful (duh!)这种继承有用的地方(呃!)
  2. Where the source object won't be modified, thus making the relation between the 2 objects a non issue.源对象不会被修改的地方,因此这两个对象之间的关系不成问题。

Example:例子:

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

Why do I consider this solution to be superior?为什么我认为这个解决方案更好? It's native, thus no looping, no recursion.它是原生的,因此没有循环,没有递归。 However, older browsers will need a polyfill.但是,较旧的浏览器将需要一个 polyfill。

There are several issues with most solutions on the internet.互联网上的大多数解决方案都存在几个问题。 So I decided to make a follow-up, which includes, why the accepted answer shouldn't be accepted.所以我决定进行跟进,其中包括为什么不应该接受已接受的答案。

starting situation起始情况

I want to deep-copy a Javascript Object with all of its children and their children and so on.我想深度复制一个 Javascript Object及其所有子对象及其子对象等等。 But since I'm not kind of a normal developer, my Object has normal properties , circular structures and even nested objects .但由于我不是一个普通的开发人员,我的Object具有普通propertiescircular structures甚至nested objects

So let's create a circular structure and a nested object first.所以让我们先创建一个circular structure和一个nested object

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

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

Let's bring everything together in an Object named a .让我们将所有内容放在一个名为aObject中。

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

Next, we want to copy a into a variable named b and mutate it.接下来,我们要将a复制到一个名为b的变量中并对其进行变异。

var b = a;

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

You know what happened here because if not you wouldn't even land on this great question.您知道这里发生了什么,因为如果不是,您甚至不会遇到这个好问题。

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

Now let's find a solution.现在让我们找到解决方案。

JSON JSON

The first attempt I tried was using JSON .我尝试的第一次尝试是使用JSON

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

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

Don't waste too much time on it, you'll get TypeError: Converting circular structure to JSON .不要在上面浪费太多时间,你会得到TypeError: Converting circular structure to JSON

Recursive copy (the accepted "answer")递归副本(接受的“答案”)

Let's have a look at the accepted answer.让我们看看接受的答案。

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

Looks good, heh?看起来不错吧? It's a recursive copy of the object and handles other types as well, like Date , but that wasn't a requirement.它是对象的递归副本,也可以处理其他类型,例如Date ,但这不是必需的。

var b = cloneSO(a);

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

Recursion and circular structures doesn't work well together... RangeError: Maximum call stack size exceeded递归和circular structures不能很好地协同工作...... RangeError: Maximum call stack size exceeded

native solution本机解决方案

After arguing with my co-worker, my boss asked us what happened, and he found a simple solution after some googling.与同事争吵后,我的老板问我们发生了什么事,他在谷歌搜索后找到了一个简单的解决方案 It's called Object.create .它被称为Object.create

var b = Object.create(a);

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

This solution was added to Javascript some time ago and even handles circular structure .这个解决方案是前段时间添加到 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"
    }
}

... and you see, it didn't work with the nested structure inside. ...你看,它不适用于内部的嵌套结构。

polyfill for the native solution原生解决方案的 polyfill

There's a polyfill for Object.create in the older browser just like the IE 8. It's something like recommended by Mozilla, and of course, it's not perfect and results in the same problem as the native solution . IE 8 一样,在旧版浏览器中有一个Object.create的 polyfill。它类似于 Mozilla 推荐的东西,当然,它并不完美,会导致与原生解决方案相同的问题。

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

var b = clonePF(a);

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

I've put F outside the scope so we can have a look at what instanceof tells us.我把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

Same problem as the native solution , but a little bit worse output.与本机解决方案相同的问题,但输出稍差一些。

the better (but not perfect) solution更好(但不完美)的解决方案

When digging around, I found a similar question ( In Javascript, when performing a deep copy, how do I avoid a cycle, due to a property being "this"? ) to this one, but with a way better solution.在挖掘时,我发现了一个类似的问题( 在 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';

And let's have a look at the output...让我们看看输出......

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

The requirements are matched, but there are still some smaller issues, including changing the instance of nested and circ to Object .需求是匹配的,但还是有一些小问题,包括将nestedcircinstance改为Object

The structure of trees that share a leaf won't be copied, they will become two independent leaves:共享叶子的树的结构不会被复制,它们将成为两个独立的叶子:

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

conclusion结论

The last solution using recursion and a cache, may not be the best, but it's a real deep-copy of the object.最后一个使用递归和缓存的解决方案可能不是最好的,但它是对象的真正深层副本。 It handles simple properties , circular structures and nested object , but it will mess up the instance of them while cloning.它处理简单的propertiescircular structuresnested object ,但在克隆时会弄乱它们的实例。

jsfiddle jsfiddle

If you're okay with a shallow copy, the underscore.js library has a clone method.如果你对浅拷贝没问题,underscore.js 库有一个克隆方法。

y = _.clone(x);

or you can extend it like或者你可以像这样扩展它

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

OK, imagine you have this object below and you want to clone it:好的,假设你在下面有这个对象并且你想克隆它:

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

or或者

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

the answer is mainly depeneds on which ECMAscript you using, in ES6+ , you can simply use Object.assign to do the clone:答案主要取决于您使用的ECMAscript ,在ES6+中,您可以简单地使用Object.assign进行克隆:

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

or using spread operator like this:或像这样使用扩展运算符:

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

But if you using ES5 , you can use few methods, but the JSON.stringify , just make sure you not using for a big chunk of data to copy, but it could be one line handy way in many cases, something like this:但是如果你使用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

Update 06 July 2020 2020 年 7 月 6 日更新

There are three (3) ways to clone objects in JavaScript.有三 (3) 种方法可以在 JavaScript 中克隆对象。 As objects in JavaScript are reference values, you can't simply just copy using the =.由于 JavaScript 中的对象是引用值,因此您不能简单地使用 = 进行复制。

The ways are:方法是:

const food = { food: 'apple', drink: 'milk' }


// 1. Using the "Spread"
// ------------------

{ ...food }


// 2. Using "Object.assign"
// ------------------

Object.assign({}, food)


// 3. "JSON"
// ------------------

JSON.parse(JSON.stringify(food))

// RESULT:
// { food: 'apple', drink: 'milk' }

This can be used as a reference summary.这可以作为参考总结。

One particularly inelegant solution is to use JSON encoding to make deep copies of objects that do not have member methods.一种特别不优雅的解决方案是使用 JSON 编码来制作没有成员方法的对象的深层副本。 The methodology is to JSON encode your target object, then by decoding it, you get the copy you are looking for.该方法是对您的目标对象进行 JSON 编码,然后通过对其进行解码,您可以获得所需的副本。 You can decode as many times as you want to make as many copies as you need.您可以根据需要进行多次解码,制作尽可能多的副本。

Of course, functions do not belong in JSON, so this only works for objects without member methods.当然,函数不属于 JSON,所以这只适用于没有成员方法的对象。

This methodology was perfect for my use case, since I'm storing JSON blobs in a key-value store, and when they are exposed as objects in a JavaScript API, each object actually contains a copy of the original state of the object so we can calculate the delta after the caller has mutated the exposed object.这种方法非常适合我的用例,因为我将 JSON blob 存储在键值存储中,当它们在 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

You can simply use a spread property to copy an object without references.您可以简单地使用扩展属性来复制没有引用的对象。 But be careful (see comments), the 'copy' is just on the lowest object/array level.但要小心(见评论),“副本”只是在最低的对象/数组级别。 Nested properties are still references!嵌套属性仍然是引用!


Complete clone:完整克隆:

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

// => mutate without references:

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

Clone with references on second level:使用二级引用克隆:

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 actually does not support deep clones natively. JavaScript 实际上本身并不支持深度克隆。 Use an utility function.使用效用函数。 For example Ramda:例如拉姆达:

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

const objClone = { ...obj };

请注意,嵌套对象仍被复制为引用。

From this article: How to copy arrays and objects in Javascript by Brian Huisman:来自这篇文章:Brian Huisman如何在 Javascript 中复制数组和对象

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

For those using AngularJS, there is also direct method for cloning or extending of the objects in this library.对于那些使用 AngularJS 的人,也有直接的方法来克隆或扩展这个库中的对象。

var destination = angular.copy(source);

or或者

angular.copy(source, destination);

More in angular.copy documentation ...更多在 angular.copy文档中......

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

A.Levy's answer is almost complete, here is my little contribution: there is a way how to handle recursive references , see this line A.Levy 的回答几乎是完整的,这是我的一点贡献:有一种方法如何处理递归引用,请参见这一行

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

If the object is XML DOM element, we must use cloneNode instead如果对象是 XML DOM 元素,我们必须使用cloneNode代替

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

Inspired by A.Levy's exhaustive study and Calvin's prototyping approach, I offer this solution:受 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;
}

See also Andy Burke's note in the answers.另请参阅答案中的安迪·伯克 (Andy Burke) 的注释。

Performance表现

Today 2020.04.30 I perform tests of chosen solutions on Chrome v81.0, Safari v13.1 and Firefox v75.0 on MacOs High Sierra v10.13.6.今天 2020.04.30 我在 MacOs High Sierra v10.13.6 上在 Chrome v81.0、Safari v13.1 和 Firefox v75.0 上执行所选解决方案的测试。

I focus on speed of copy DATA (object with simple type fields, not methods etc.).我专注于复制 DATA 的速度(具有简单类型字段的对象,而不是方法等)。 The solutions AI can make only shallow copy, solutions JU can make deep copy.解AI只能做浅拷贝,解JU可以做深拷贝。

Results for shallow copy浅拷贝的结果

  • solution {...obj} (A) is fastest on chrome and firefox and medium fast on safari解决方案{...obj} (A) 在 chrome 和 firefox 上最快,在 safari 上中等速度
  • solution based on Object.assign (B) is fast on all browsers基于Object.assign (B) 的解决方案在所有浏览器上都很快
  • jQuery (E) and lodash (F,G,H) solutions are medium/quite fast jQuery (E) 和 lodash (F,G,H) 解决方案中等/相当快
  • solution JSON.parse/stringify (K) is quite slow解决方案JSON.parse/stringify (K) 相当慢
  • solutions D and U are slow on all browsers解决方案 D 和 U 在所有浏览器上都很慢

在此处输入图像描述

Results for deep copy深拷贝的结果

  • solution Q is fastest on all browsers解决方案 Q 在所有浏览器上最快
  • jQuery (L) and lodash (J) are medium fast jQuery (L) 和 lodash (J) 中等速度
  • solution JSON.parse/stringify (K) is quite slow解决方案JSON.parse/stringify (K) 相当慢
  • solution U is slowest on all browsers解决方案 U 在所有浏览器上最慢
  • lodash (J) and solution U crash on Chrome for 1000 level deep object lodash (J) 和解决方案 U crash on Chrome for 1000 level deep object

在此处输入图像描述

Details细节

For choosen solutions: A B C(my) D E F G H I J K L M N O P Q R S T U , I perform 4 tests对于选择的解决方案: A B C(my) D E F G H I J K L M N O P Q R S T U ,我执行 4 次测试

  • shallow-small: object with 10 non-nested fields - you can run it HERE浅小:具有 10 个非嵌套字段的对象 - 您可以在此处运行它
  • shallow-big: object with 1000 non-nested fields - you can run it HERE浅大:具有 1000 个非嵌套字段的对象 - 您可以在此处运行它
  • deep-small: object with 10 levels-nested fields - you can run it HERE deep-small:具有 10 个级别嵌套字段的对象 - 您可以在此处运行它
  • deep-big: object with 1000 levels-nested fields - you can run it HERE deep-big:具有 1000 个级别嵌套字段的对象 - 您可以在此处运行它

Objects used in tests are show in below snippet测试中使用的对象显示在下面的代码片段中

 let obj_ShallowSmall = { field0: false, field1: true, field2: 1, field3: 0, field4: null, field5: [], field6: {}, field7: "text7", field8: "text8", } let obj_DeepSmall = { level0: { level1: { level2: { level3: { level4: { level5: { level6: { level7: { level8: { level9: [[[[[[[[[['abc']]]]]]]]]], }}}}}}}}}, }; let obj_ShallowBig = Array(1000).fill(0).reduce((a,c,i) => (a['field'+i]=getField(i),a) ,{}); let obj_DeepBig = genDeepObject(1000); // ------------------ // Show objects // ------------------ console.log('obj_ShallowSmall:',JSON.stringify(obj_ShallowSmall)); console.log('obj_DeepSmall:',JSON.stringify(obj_DeepSmall)); console.log('obj_ShallowBig:',JSON.stringify(obj_ShallowBig)); console.log('obj_DeepBig:',JSON.stringify(obj_DeepBig)); // ------------------ // HELPERS // ------------------ function getField(k) { let i=k%10; if(i==0) return false; if(i==1) return true; if(i==2) return k; if(i==3) return 0; if(i==4) return null; if(i==5) return []; if(i==6) return {}; if(i>=7) return "text"+k; } function genDeepObject(N) { // generate: {level0:{level1:{...levelN: {end:[[[...N-times...['abc']...]]] }}}...}}} let obj={}; let o=obj; let arr = []; let a=arr; for(let i=0; i<N; i++) { o['level'+i]={}; o=o['level'+i]; let aa=[]; a.push(aa); a=aa; } a[0]='abc'; o['end']=arr; return obj; }

Below snippet presents tested solutions and shows differences between them下面的代码片段展示了经过测试的解决方案并显示了它们之间的差异

 function A(obj) { return {...obj} } function B(obj) { return Object.assign({}, obj); } function C(obj) { return Object.keys(obj).reduce( (a,c) => (a[c]=obj[c], a), {}) } function D(obj) { let copyOfObject = {}; Object.defineProperties(copyOfObject, Object.getOwnPropertyDescriptors(obj)); return copyOfObject; } function E(obj) { return jQuery.extend({}, obj) // shallow } function F(obj) { return _.clone(obj); } function G(obj) { return _.clone(obj,true); } function H(obj) { return _.extend({},obj); } function I(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; } function J(obj) { return _.cloneDeep(obj,true); } function K(obj) { return JSON.parse(JSON.stringify(obj)); } function L(obj) { return jQuery.extend(true, {}, obj) // deep } function M(obj) { if(obj == null || typeof(obj) != 'object') return obj; var temp = new obj.constructor(); for(var key in obj) temp[key] = M(obj[key]); return temp; } function N(obj) { let EClone = function(obj) { var newObj = (obj instanceof Array) ? [] : {}; for (var i in obj) { if (i == 'EClone') continue; if (obj[i] && typeof obj[i] == "object") { newObj[i] = EClone(obj[i]); } else newObj[i] = obj[i] } return newObj; }; return EClone(obj); }; function O(obj) { if (obj == null || typeof obj != "object") return obj; if (obj.constructor != Object && obj.constructor != Array) return obj; if (obj.constructor == Date || obj.constructor == RegExp || obj.constructor == Function || obj.constructor == String || obj.constructor == Number || obj.constructor == Boolean) return new obj.constructor(obj); let to = new obj.constructor(); for (var name in obj) { to[name] = typeof to[name] == "undefined" ? O(obj[name], null) : to[name]; } return to; } function P(obj) { 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; } return clone({},obj); } function Q(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] = Q(obj[i]); } return copy; } // Handle Object if (obj instanceof Object) { copy = {}; for (var attr in obj) { if (obj.hasOwnProperty(attr)) copy[attr] = Q(obj[attr]); } return copy; } throw new Error("Unable to copy obj! Its type isn't supported."); } function R(obj) { const gdcc = "__getDeepCircularCopy__"; if (obj !== Object(obj)) { return obj; // primitive value } var set = gdcc in obj, cache = obj[gdcc], result; if (set && typeof cache == "function") { return cache(); } // else obj[gdcc] = function() { return result; }; // overwrite if (obj instanceof Array) { result = []; for (var i=0; i<obj.length; i++) { result[i] = R(obj[i]); } } else { result = {}; for (var prop in obj) if (prop != gdcc) result[prop] = R(obj[prop]); else if (set) result[prop] = R(cache); } if (set) { obj[gdcc] = cache; // reset } else { delete obj[gdcc]; // unset again } return result; } function S(obj) { const cache = new WeakMap(); // Map of old - new references function copy(object) { if (typeof object !== 'object' || object === null || object instanceof HTMLElement ) return object; // primitive value or HTMLElement if (object instanceof Date) return new Date().setTime(object.getTime()); if (object instanceof RegExp) return new RegExp(object.source, object.flags); if (cache.has(object)) return cache.get(object); const result = object instanceof Array ? [] : {}; cache.set(object, result); // store reference to object before the recursive starts if (object instanceof Array) { for(const o of object) { result.push(copy(o)); } return result; } const keys = Object.keys(object); for (const key of keys) result[key] = copy(object[key]); return result; } return copy(obj); } function T(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; } function U(obj) { /* Deep copy objects by value rather than by reference, exception: `Proxy` */ const seen = new WeakMap() return clone(obj) function defineProp(object, key, descriptor = {}, copyFrom = {}) { const { configurable: _configurable, writable: _writable } = Object.getOwnPropertyDescriptor(object, key) || { configurable: true, writable: true } const test = _configurable // Can redefine property && (_writable === undefined || _writable) // Can assign to property if (!test || arguments.length <= 2) return test const basisDesc = Object.getOwnPropertyDescriptor(copyFrom, key) || { configurable: true, writable: true } // Custom… || {}; // …or left to native default settings ["get", "set", "value", "writable", "enumerable", "configurable"] .forEach(attr => descriptor[attr] === undefined && (descriptor[attr] = basisDesc[attr]) ) const { get, set, value, writable, enumerable, configurable } = descriptor return Object.defineProperty(object, key, { enumerable, configurable, ...get || set ? { get, set } // Accessor descriptor : { value, writable } // Data descriptor }) } function clone(object) { if (object !== Object(object)) return object /* —— Check if the object belongs to a primitive data type */ if (object instanceof Node) return object.cloneNode(true) /* —— Clone DOM trees */ let _object // The clone of object switch (object.constructor) { case Array: case Object: _object = cloneObject(object) break case Date: _object = new Date(+object) break case Function: const fnStr = String(object) _object = new Function("return " + (/^(?!function |[^{]+?=>)[^(]+?\(/.test(fnStr) ? "function " : "" ) + fnStr )() copyPropDescs(_object, object) break case RegExp: _object = new RegExp(object) break default: switch (Object.prototype.toString.call(object.constructor)) { // // Stem from: case "[object Function]": // `class` case "[object Undefined]": // `Object.create(null)` _object = cloneObject(object) break default: // `Proxy` _object = object } } return _object } function cloneObject(object) { if (seen.has(object)) return seen.get(object) /* —— Handle recursive references (circular structures) */ const _object = Array.isArray(object) ? [] : Object.create(Object.getPrototypeOf(object)) /* —— Assign [[Prototype]] for inheritance */ seen.set(object, _object) /* —— Make `_object` the associative mirror of `object` */ Reflect.ownKeys(object).forEach(key => defineProp(_object, key, { value: clone(object[key]) }, object) ) return _object } function copyPropDescs(target, source) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source) ) } } // ------------------------ // Test properties // ------------------------ console.log(` shallow deep func circ undefined date RegExp bigInt`) log(A); log(B); log(C); log(D); log(E); log(F); log(G); log(H); log(I); log(J); log(K); log(L); log(M); log(N); log(O); log(P); log(Q); log(R); log(S); log(T); log(U); console.log(` shallow deep func circ undefined date RegExp bigInt ---- LEGEND: shallow - solution create shallow copy deep - solution create deep copy func - solution copy functions circ - solution can copy object with circular references undefined - solution copy fields with undefined value date - solution can copy date RegExp - solution can copy fields with regular expressions bigInt - solution can copy BigInt `) // ------------------------ // Helper functions // ------------------------ function deepCompare(obj1,obj2) { return JSON.stringify(obj1)===JSON.stringify(obj2); } function getCase() { // pure data case return { undef: undefined, bool: true, num: 1, str: "txt1", e1: null, e2: [], e3: {}, e4: 0, e5: false, arr: [ false, 2, "txt3", null, [], {}, [ true,4,"txt5",null, [], {}, [true,6,"txt7",null,[],{} ], {bool: true,num: 8, str: "txt9", e1:null, e2:[] ,e3:{} ,e4: 0, e5: false} ], {bool: true,num: 10, str: "txt11", e1:null, e2:[] ,e3:{} ,e4: 0, e5: false} ], obj: { bool: true, num: 12, str: "txt13", e1: null, e2: [], e3: {}, e4: 0, e5: false, arr: [true,14,"txt15",null,[],{} ], obj: { bool: true, num: 16, str: "txt17", e1: null, e2: [], e3: {}, e4: 0, e5: false, arr: [true,18,"txt19",null,[],{} ], obj: {bool: true,num: 20, str: "txt21", e1:null, e2:[] ,e3:{} ,e4: 0, e5: false} } } }; } function check(org, copy, field, newValue) { copy[field] = newValue; return deepCompare(org,copy); } function testFunc(f) { let o = { a:1, fun: (i,j)=> i+j }; let c = f(o); let val = false try{ val = c.fun(3,4)==7; } catch(e) { } return val; } function testCirc(f) { function Circ() { this.me = this; } var o = { x: 'a', circ: new Circ(), obj_circ: null, }; o.obj_circ = o; let val = false; try{ let c = f(o); val = (o.obj_circ == o) && (o.circ == o.circ.me); } catch(e) { } return val; } function testRegExp(f) { let o = { re: /a[0-9]+/, }; let val = false; try{ let c = f(o); val = (String(c.re) == String(/a[0-9]+/)); } catch(e) { } return val; } function testDate(f) { let o = { date: new Date(), }; let val = false; try{ let c = f(o); val = (+new Date(c.date) == +new Date(o.date)); } catch(e) { } return val; } function testBigInt(f) { let val = false; try{ let o = { big: 123n, }; let c = f(o); val = o.big == c.big; } catch(e) { } return val; } function log(f) { let o = getCase(); // orginal object let oB = getCase(); // "backup" used for shallow valid test let c1 = f(o); // copy 1 for reference let c2 = f(o); // copy 2 for test shallow values let c3 = f(o); // copy 3 for test deep values let is_proper_copy = deepCompare(c1,o); // shoud be true // shallow changes let testShallow = [ ['bool',false],['num',666],['str','xyz'],['arr',[]],['obj',{}] ] .reduce((acc,curr)=> acc && check(c1,c2,curr[0], curr[1]), true ); // should be true (original object shoud not have changed shallow fields) let is_valid = deepCompare(o,oB); // deep test (intruduce some change) if (c3.arr[6]) c3.arr[6][7].num = 777; let diff_shallow = !testShallow; // shoud be true (shallow field was copied) let diff_deep = !deepCompare(c1,c3); // shoud be true (deep field was copied) let can_copy_functions = testFunc(f); let can_copy_circular = testCirc(f); let can_copy_regexp = testRegExp(f); let can_copy_date = testDate(f); let can_copy_bigInt = testBigInt(f); let has_undefined = 'undef' in c1; // field with undefined value is copied? let is_ok = is_valid && is_proper_copy; let b=(bool) => (bool+'').padEnd(5,' '); // bool value to formated string testFunc(f); if(is_ok) { console.log(`${f.name} ${b(diff_shallow)} ${b(diff_deep)} ${b(can_copy_functions)} ${b(can_copy_circular)} ${b(has_undefined)} ${b(can_copy_date)} ${b(can_copy_regexp)} ${b(can_copy_bigInt)}`) } else { console.log(`${f.name}: INVALID ${is_valid} ${is_proper_copy}`,{c1}) } }
 <script src="https://code.jquery.com/jquery-3.5.0.min.js" integrity="sha256-xNzN2a4ltkB44Mc/Jz3pT4iU1cmeR0FkXs4pru/JxaQ=" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.15/lodash.min.js"></script> This snippet only presents tested solutions and show differences between them (but it no make performence tests)

Below there are example results for Chrome for shallow-big object下面是 Chrome 用于浅大对象的示例结果

在此处输入图像描述

使用 Lodash:

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

In ES-6 you can simply use Object.assign(...).在 ES-6 中,您可以简单地使用 Object.assign(...)。 Ex:前任:

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

A good reference is here: https://googlechrome.github.io/samples/object-assign-es6/一个很好的参考在这里: https ://googlechrome.github.io/samples/object-assign-es6/

Interested in cloning simple objects:对克隆简单对象感兴趣:

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

Source : How to copy JavaScript object to new variable NOT by reference?来源: 如何不通过引用将 JavaScript 对象复制到新变量?

You can clone an object and remove any reference from the previous one using a single line of code.您可以使用一行代码克隆一个对象并从前一个对象中删除任何引用。 Simply do:只需这样做:

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

For browsers / engines that do not currently support Object.create you can use this polyfill:对于当前不支持 Object.create 的浏览器/引擎,您可以使用这个 polyfill:

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

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

New answer to an old question!老问题的新答案! If you have the pleasure of having using ECMAScript 2016 (ES6) with Spread Syntax , it's easy.如果您有幸使用 ECMAScript 2016 (ES6) 和Spread Syntax ,这很容易。

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

This provides a clean method for a shallow copy of an object.这为对象的浅拷贝提供了一种干净的方法。 Making a deep copy, meaning makign a new copy of every value in every recursively nested object, requires on of the heavier solutions above.制作深层副本,即为每个递归嵌套对象中的每个值创建一个新副本,需要上述较重的解决方案。

JavaScript keeps evolving. JavaScript 不断发展。

Structured Cloning结构化克隆

2022 update: The structuredClone() global function is already available in Node 17, Deno 1.14, and most major browsers (see Can I Use ). 2022 年更新: structuredClone()全局函数已在 Node 17、Deno 1.14 和大多数主要浏览器中可用(请参阅我可以使用)。

You can use the same structured clone mechanism that the HTML standard includes for sending data between realms.可以使用 HTML 标准包含的相同结构化克隆机制在领域之间发送数据。

const clone = structuredClone(original);

See the other answer for more details.有关更多详细信息,请参阅其他答案

I think there is a simple and working answer.我认为有一个简单而有效的答案。 In deep copying there are two concerns:在深度复制中,有两个问题:

  1. Keep properties independent to each other.保持属性相互独立。
  2. And keep the methods alive on cloned object.并使方法在克隆对象上保持活动状态。

So I think one simple solution will be to first serialize and deserialize and then do an assign on it to copy functions too.所以我认为一个简单的解决方案是首先序列化和反序列化,然后对其进行分配以复制函数。

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

Although this question has many answers, I hope this one helps too.虽然这个问题有很多答案,但我希望这个也有帮助。

For a deep copy and clone, JSON.stringify then JSON.parse the object:对于深拷贝和克隆,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}}

(The following was mainly an integration of @ Maciej Bukowski , @ A. Levy , @ Jan Turoň , @ Redu 's answers, and @ LeviRoberts , @ RobG 's comments, many thanks to them!!!) (以下主要整合了@Maciej Bukowski 、@ A. Levy 、@ Jan Turoň @Redu 的回答,以及@ LeviRoberts 、@ RobG的评论,多谢他们!!!)

Deep copy ?深拷贝 — YES! - 是的! (mostly); (大多);
Shallow copy ?浅拷贝 — NO! - 不! (except Proxy ). Proxy除外)。

I sincerely welcome everyone to test clone() .真诚欢迎大家测试clone()
In addition, defineProp() is designed to easily and quickly (re)define or copy any type of descriptor.此外, defineProp()旨在轻松快速地(重新)定义或复制任何类型的描述符。

Function功能

function clone(object) {
  /*
    Deep copy objects by value rather than by reference,
    exception: `Proxy`
  */

  const seen = new WeakMap()

  return clone(object)


  function clone(object) {
    if (object !== Object(object)) return object /*
    —— Check if the object belongs to a primitive data type */

    if (object instanceof Node) return object.cloneNode(true) /*
    —— Clone DOM trees */

    let _object // The clone of object

    switch (object.constructor) {
      case Array:
      case Object:
        _object = cloneObject(object)
        break

      case Date:
        _object = new Date(+object)
        break

      case Function:
        _object = copyFn(object)
        break

      case RegExp:
        _object = new RegExp(object)
        break

      default:
        switch (Object.prototype.toString.call(object.constructor)) {
          //                                  // Stem from:
          case "[object Function]":
            switch (object[Symbol.toStringTag]) {
              case undefined:
                _object = cloneObject(object) // `class`
                break

              case "AsyncFunction":
              case "GeneratorFunction":
              case "AsyncGeneratorFunction":
                _object = copyFn(object)
                break

              default:
                _object = object
            }
            break

          case "[object Undefined]":          // `Object.create(null)`
            _object = cloneObject(object)
            break

          default:
            _object = object                  // `Proxy`
        }
    }

    return _object
  }


  function cloneObject(object) {
    if (seen.has(object)) return seen.get(object) /*
    —— Handle recursive references (circular structures) */

    const _object = Array.isArray(object)
      ? []
      : Object.create(Object.getPrototypeOf(object)) /*
        —— Assign [[Prototype]] for inheritance */

    seen.set(object, _object) /*
    —— Make `_object` the associative mirror of `object` */

    Reflect.ownKeys(object).forEach(key =>
      defineProp(_object, key, { value: clone(object[key]) }, object)
    )

    return _object
  }
}


function copyPropDescs(target, source) {
  Object.defineProperties(target,
    Object.getOwnPropertyDescriptors(source)
  )
}


function convertFnToStr(fn) {
  let fnStr = String(fn)
  if (fn.name.startsWith("[")) // isSymbolKey
    fnStr = fnStr.replace(/\[Symbol\..+?\]/, '')
  fnStr = /^(?!(async )?(function\b|[^{]+?=>))[^(]+?\(/.test(fnStr)
    ? fnStr.replace(/^(async )?(\*)?/, "$1function$2 ") : fnStr
  return fnStr
}

function copyFn(fn) {
  const newFn = new Function(`return ${convertFnToStr(fn)}`)()
  copyPropDescs(newFn, fn)
  return newFn
}



function defineProp(object, key, descriptor = {}, copyFrom = {}) {
  const { configurable: _configurable, writable: _writable }
    = Object.getOwnPropertyDescriptor(object, key)
    || { configurable: true, writable: true }

  const test = _configurable // Can redefine property
    && (_writable === undefined || _writable) // Can assign to property

  if (!test || arguments.length <= 2) return test

  const basisDesc = Object.getOwnPropertyDescriptor(copyFrom, key)
    || { configurable: true, writable: true } // Custom…
    || {}; // …or left to native default settings

  ["get", "set", "value", "writable", "enumerable", "configurable"]
    .forEach(attr =>
      descriptor[attr] === undefined &&
      (descriptor[attr] = basisDesc[attr])
    )

  const { get, set, value, writable, enumerable, configurable }
    = descriptor

  return Object.defineProperty(object, key, {
    enumerable, configurable, ...get || set
      ? { get, set } // Accessor descriptor
      : { value, writable } // Data descriptor
  })
}

// Tests // 测试

const obj0 = {
  u: undefined,
  nul: null,
  t: true,
  num: 9,
  str: "",
  sym: Symbol("symbol"),
  [Symbol("e")]: Math.E,
  arr: [[0], [1, 2]],
  d: new Date(),
  re: /f/g,
  get g() { return 0 },
  o: {
    n: 0,
    o: { f: function (...args) { } }
  },
  f: {
    getAccessorStr(object) {
      return []
        .concat(...
          Object.values(Object.getOwnPropertyDescriptors(object))
            .filter(desc => desc.writable === undefined)
            .map(desc => Object.values(desc))
        )
        .filter(prop => typeof prop === "function")
        .map(String)
    },
    f0: function f0() { },
    f1: function () { },
    f2: a => a / (a + 1),
    f3: () => 0,
    f4(params) { return param => param + params },
    f5: (a, b) => ({ c = 0 } = {}) => a + b + c
  }
}

defineProp(obj0, "s", { set(v) { this._s = v } })
defineProp(obj0.arr, "tint", { value: { is: "non-enumerable" } })
obj0.arr[0].name = "nested array"


let obj1 = clone(obj0)
obj1.o.n = 1
obj1.o.o.g = function g(a = 0, b = 0) { return a + b }
obj1.arr[1][1] = 3
obj1.d.setTime(+obj0.d + 60 * 1000)
obj1.arr.tint.is = "enumerable? no"
obj1.arr[0].name = "a nested arr"
defineProp(obj1, "s", { set(v) { this._s = v + 1 } })
defineProp(obj1.re, "multiline", { value: true })

console.log("\n\n" + "-".repeat(2 ** 6))




console.log(">:>: Test - Routinely")

console.log("obj0:\n ", JSON.stringify(obj0))
console.log("obj1:\n ", JSON.stringify(obj1))
console.log()

console.log("obj0:\n ", obj0)
console.log("obj1:\n ", obj1)
console.log()

console.log("obj0\n ",
  ".arr.tint:", obj0.arr.tint, "\n ",
  ".arr[0].name:", obj0.arr[0].name
)
console.log("obj1\n ",
  ".arr.tint:", obj1.arr.tint, "\n ",
  ".arr[0].name:", obj1.arr[0].name
)
console.log()

console.log("Accessor-type descriptor\n ",
  "of obj0:", obj0.f.getAccessorStr(obj0), "\n ",
  "of obj1:", obj1.f.getAccessorStr(obj1), "\n ",
  "set (obj0 & obj1) .s :", obj0.s = obj1.s = 0, "\n ",
  "  → (obj0 , obj1) ._s:", obj0._s, ",", obj1._s
)

console.log("—— obj0 has not been interfered.")

console.log("\n\n" + "-".repeat(2 ** 6))




console.log(">:>: Test - More kinds of functions")

const fnsForTest = {
  f(_) { return _ },
  func: _ => _,
  aFunc: async _ => _,
  async function() { },
  async asyncFunc() { },
  aFn: async function () { },
  *gen() { },
  async *asyncGen() { },
  aG1: async function* () { },
  aG2: async function* gen() { },
  *[Symbol.iterator]() { yield* Object.keys(this) }
}

console.log(Reflect.ownKeys(fnsForTest).map(k =>
  `${String(k)}:
  ${fnsForTest[k].name}-->
    ${String(fnsForTest[k])}`
).join("\n"))

const normedFnsStr = `{
  f: function f(_) { return _ },
  func: _ => _,
  aFunc: async _ => _,
  function: async function() { },
  asyncFunc: async function asyncFunc() { },
  aFn: async function () { },
  gen: function* gen() { },
  asyncGen: async function* asyncGen() { },
  aG1: async function* () { },
  aG2: async function* gen() { },
  [Symbol.iterator]: function* () { yield* Object.keys(this) }
}`

const copiedFnsForTest = clone(fnsForTest)
console.log("fnsForTest:", fnsForTest)
console.log("fnsForTest (copied):", copiedFnsForTest)
console.log("fnsForTest (normed str):", eval(`(${normedFnsStr})`))
console.log("Comparison of fnsForTest and its clone:",
  Reflect.ownKeys(fnsForTest).map(k =>
    [k, fnsForTest[k] === copiedFnsForTest[k]]
  )
)

console.log("\n\n" + "-".repeat(2 ** 6))




console.log(">:>: Test - Circular structures")

obj0.o.r = {}
obj0.o.r.recursion = obj0.o
obj0.arr[1] = obj0.arr

obj1 = clone(obj0)
console.log("obj0:\n ", obj0)
console.log("obj1:\n ", obj1)

console.log("Clear obj0's recursion:",
  obj0.o.r.recursion = null, obj0.arr[1] = 1
)
console.log(
  "obj0\n ",
  ".o.r:", obj0.o.r, "\n ",
  ".arr:", obj0.arr
)
console.log(
  "obj1\n ",
  ".o.r:", obj1.o.r, "\n ",
  ".arr:", obj1.arr
)
console.log("—— obj1 has not been interfered.")


console.log("\n\n" + "-".repeat(2 ** 6))




console.log(">:>: Test - Classes")

class Person {
  constructor(name) {
    this.name = name
  }
}

class Boy extends Person { }
Boy.prototype.sex = "M"

const boy0 = new Boy
boy0.hobby = { sport: "spaceflight" }

const boy1 = clone(boy0)
boy1.hobby.sport = "superluminal flight"

boy0.name = "one"
boy1.name = "neo"

console.log("boy0:\n ", boy0)
console.log("boy1:\n ", boy1)
console.log("boy1's prototype === boy0's:",
  Object.getPrototypeOf(boy1) === Object.getPrototypeOf(boy0)
)

References参考

  1. Object.create() | Object.create() | MDN MDN
  2. Object.defineProperties() | Object.defineProperties() | MDN MDN
  3. Enumerability and ownership of properties | 属性的可枚举性和所有权 | MDN MDN
  4. TypeError: cyclic object value | TypeError: 循环对象值 | MDN MDN

Language tricks used使用的语言技巧

  1. Conditionally add prop to object有条件地向对象添加道具

Use lodash _.cloneDeep().使用 lodash _.cloneDeep()。

Shallow Copy: lodash _.clone()浅拷贝:lodash _.clone()

A shallow copy can be made by simply copying the reference.可以通过简单地复制引用来进行浅拷贝。

let obj1 = {
    a: 0,
    b: {
        c: 0,
        e: {
            f: 0
        }
    }
};
let obj3 = _.clone(obj1);
obj1.a = 4;
obj1.b.c = 4;
obj1.b.e.f = 100;

console.log(JSON.stringify(obj1));
//{"a":4,"b":{"c":4,"e":{"f":100}}}

console.log(JSON.stringify(obj3));
//{"a":0,"b":{"c":4,"e":{"f":100}}}

浅拷贝:lodash _.clone()

Deep Copy: lodash _.cloneDeep()深拷贝:lodash _.cloneDeep()

fields are dereferenced: rather than references to objects being copied字段被取消引用:而不是对被复制对象的引用

let obj1 = {
    a: 0,
    b: {
        c: 0,
        e: {
            f: 0
        }
    }
};
let obj3 = _.cloneDeep(obj1);
obj1.a = 100;
obj1.b.c = 100;
obj1.b.e.f = 100;

console.log(JSON.stringify(obj1));
{"a":100,"b":{"c":100,"e":{"f":100}}}

console.log(JSON.stringify(obj3));
{"a":0,"b":{"c":0,"e":{"f":0}}}

深拷贝:lodash _.cloneDeep()

The most correct to copy object is use Object.create :最正确的复制对象是使用Object.create

Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));

Such notation will make identically the same object with correct prototype and hidden properties.这样的符号将使相同的对象具有正确的原型和隐藏属性。

This is an adaptation of A. Levy's code to also handle the cloning of functions and multiple/cyclic references - what this means is that if two properties in the tree which is cloned are references of the same object, the cloned object tree will have these properties point to one and the same clone of the referenced object.这是对 A. Levy 的代码的改编,也可以处理函数的克隆和多个/循环引用 - 这意味着如果被克隆的树中的两个属性是同一对象的引用,则克隆的对象树将具有这些属性指向被引用对象的同一个克隆。 This also solves the case of cyclic dependencies which, if left unhandled, leads to an infinite loop.这也解决了循环依赖的情况,如果不加以处理,就会导致无限循环。 The complexity of the algorithm is O(n)算法的复杂度为 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;
}

Some quick tests一些快速测试

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

I just wanted to add to all the Object.create solutions in this post, that this does not work in the desired way with nodejs.我只是想在这篇文章中添加到所有Object.create解决方案中,这不适用于 nodejs。

In Firefox the result of在 Firefox 中的结果

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

is

{test:"test"} . {test:"test"}

In nodejs it is在nodejs中是

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

Since mindeavor stated that the object to be cloned is a 'literal-constructed' object, a solution might be to simply generate the object multiple times rather than cloning an instance of the object:由于mindeavor声明要克隆的对象是“文字构造”对象,因此解决方案可能是简单地多次生成对象而不是克隆对象的实例:

function createMyObject()
{
    var myObject =
    {
        ...
    };
    return myObject;
}

var myObjectInstance1 = createMyObject();
var myObjectInstance2 = createMyObject();

I've written my own implementation.我已经编写了自己的实现。 Not sure if it counts as a better solution:不确定它是否算作更好的解决方案:

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

Following is the implementation:以下是实现:

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

Use deepcopy from npm .使用来自npmdeepcopy Works in both the browser and in node as an npm module...作为npm module...在浏览器和node中工作......

https://www.npmjs.com/package/deepcopy https://www.npmjs.com/package/deepcopy

let a = deepcopy(b)

Object copy using ( ... )使用 ( ... ) 复制对象

//bad
const original = { a: 1, b: 2 };
const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2,c: 3 }

//good
const originalObj = { id: 5, name: 'San Francisco'};
const copyObject = {...originalObj, pincode: 4444};
console.log(copyObject)  //{ id: 5, name: 'San Francisco', pincode: 4444 }

Same can be use for copying array from one to other同样可用于将数组从一个复制到另一个

const itemsCopy = [...items];

Simple recursive method to clone an object.克隆对象的简单递归方法。 Also could use lodash.clone.也可以使用 lodash.clone。

 let clone = (obj) => { let obj2 = Array.isArray(obj) ? [] : {}; for(let k in obj) { obj2[k] = (typeof obj[k] === 'object' ) ? clone(obj[k]) : obj[k]; } return obj2; } let w = { name: "Apple", types: ["Fuji", "Gala"]}; let x = clone(w); w.name = "Orange"; w.types = ["Navel"]; console.log(x); console.log(w);

Native JS:原生 JS:

const shallowClone = {...originalObj};
const deepClone = JSON.parse(JSON.stringify(originalObj));

Using Libraries:使用库:

// Lodash
const shallowClone = _.clone(originalObj);
const deepClone = _. cloneDeep(originalObj);

// JQuery
const shallowClone = jQuery.extend({}, originalObj);
const deepClone = jQuery.extend(true, {}, originalObj);

// Angular
const deepClone = angular.copy(originalObj);

Jan Turoň's answer above is very close, and may be the best to use in a browser due to compatibility issues, but it will potentially cause some strange enumeration issues. Jan Turoň 上面的答案非常接近,由于兼容性问题,可能最好在浏览器中使用,但它可能会导致一些奇怪的枚举问题。 For instance, executing:例如,执行:

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

Will assign the clone() method to i after iterating through the elements of the array.在遍历数组的元素后将 clone() 方法分配给 i 。 Here's an adaptation that avoids the enumeration and works with node.js:这是避免枚举并与 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; } } );

This avoids making the clone() method enumerable because defineProperty() defaults enumerable to false.这避免了使 clone() 方法可枚举,因为 defineProperty() 默认可枚举为 false。

Consult http://www.w3.org/html/wg/drafts/html/master/infrastructure.html#safe-passing-of-structured-data for the W3C's "Safe passing of structured data" algorithm, intended to be implemented by browsers for passing data to eg web workers.请参阅http://www.w3.org/html/wg/drafts/html/master/infrastructure.html#safe-passing-of-structured-data了解 W3C 的“结构化数据安全传递”算法,该算法旨在实现通过浏览器将数据传递给例如网络工作者。 However, it has some limitations, in that it does not handle functions.但是,它有一些限制,因为它不处理函数。 See https://developer.mozilla.org/en-US/docs/DOM/The_structured_clone_algorithm for more information, including an alternative algorithm in JS which gets you part of the way there.有关更多信息,请参阅https://developer.mozilla.org/en-US/docs/DOM/The_structured_clone_algorithm ,包括 JS 中的一种替代算法,它可以让您部分实现。

Clone an object based on a template .根据template克隆对象。 What do you do if you don't want an exact copy, but you do want the robustness of some kind of reliable clone operation but you only want bits cloned or you want to make sure you can control the existence or format of each attribute value cloned?如果您不想要一个精确的副本,但您确实想要某种可靠克隆操作的稳健性但您只想要克隆位,或者您想确保您可以控制每个属性值的存在或格式,您会怎么做克隆?

I am contributing this because it's useful for us and we created it because we could not find something similar.我贡献这个是因为它对我们有用,我们创建它是因为我们找不到类似的东西。 You can use it to clone an object based on a template object which specifies what attributes of the object I want to clone, and the template allows for functions to transform those attributes into something different if they don't exist on the source object or however you want to handle the clone.您可以使用它来克隆基于template对象的对象,该模板对象指定我要克隆的对象的哪些属性,并且模板允许函数将这些属性转换为不同的东西,如果它们不存在于源对象上,或者但是你想处理克隆。 If it's not useful I am sure someone can delete this answer.如果它没有用,我相信有人可以删除这个答案。

   function isFunction(functionToCheck) {
       var getType = {};
       return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]';
   }

   function cloneObjectByTemplate(obj, tpl, cloneConstructor) {
       if (typeof cloneConstructor === "undefined") {
           cloneConstructor = false;
       }
       if (obj == null || typeof (obj) != 'object') return obj;

       //if we have an array, work through it's contents and apply the template to each item...
       if (Array.isArray(obj)) {
           var ret = [];
           for (var i = 0; i < obj.length; i++) {
               ret.push(cloneObjectByTemplate(obj[i], tpl, cloneConstructor));
           }
           return ret;
       }

       //otherwise we have an object...
       //var temp:any = {}; // obj.constructor(); // we can't call obj.constructor because typescript defines this, so if we are dealing with a typescript object it might reset values.
       var temp = cloneConstructor ? new obj.constructor() : {};

       for (var key in tpl) {
           //if we are provided with a function to determine the value of this property, call it...
           if (isFunction(tpl[key])) {
               temp[key] = tpl[key](obj); //assign the result of the function call, passing in the value
           } else {
               //if our object has this property...
               if (obj[key] != undefined) {
                   if (Array.isArray(obj[key])) {
                       temp[key] = [];
                       for (var i = 0; i < obj[key].length; i++) {
                           temp[key].push(cloneObjectByTemplate(obj[key][i], tpl[key], cloneConstructor));
                       }
                   } else {
                       temp[key] = cloneObjectByTemplate(obj[key], tpl[key], cloneConstructor);
                   }
               }
           }
       }

       return temp;
   }

A simple way to call it would be like this:一个简单的调用方法是这样的:

var source = {
       a: "whatever",
       b: {
           x: "yeah",
           y: "haha"
       }
   };
   var template = {
       a: true, //we want to clone "a"
       b: {
           x: true //we want to clone "b.x" too
       }
   }; 
   var destination = cloneObjectByTemplate(source, template);

If you wanted to use a function to make sure an attribute is returned or to make sure it's a particular type, use a template like this.如果您想使用函数来确保返回属性或确保它是特定类型,请使用这样的模板。 Instead of using { ID: true } we are providing a function which still just copies the ID attribute of the source object but it makes sure that it's a number even if it does not exist on the source object.我们提供了一个函数,而不是使用{ ID: true } ,它仍然只是复制源对象的ID attribute ,但它确保它是一个数字,即使它不存在于源对象上。

 var template = {
    ID: function (srcObj) {
        if(srcObj.ID == undefined){ return -1; }
        return parseInt(srcObj.ID.toString());
    }
}

Arrays will clone fine but if you want to you can have your own function handle those individual attributes too, and do something special like this:数组可以很好地克隆,但如果您愿意,您也可以让自己的函数处理这些单独的属性,并执行以下特殊操作:

 var template = {
    tags: function (srcObj) {
        var tags = [];
        if (process.tags != undefined) {
            for (var i = 0; i < process.tags.length; i++) {

                tags.push(cloneObjectByTemplate(
                  srcObj.tags[i],
                  { a : true, b : true } //another template for each item in the array
                );
            }
        }
        return tags;
    }
 }

So in the above, our template just copies the tags attribute of the source object if it exists, (it's assumed to be an array), and for each element in that array the clone function is called to individually clone it based on a second template which just copies the a and b attributes of each of those tag elements.所以在上面,我们的模板只是复制源对象的tags属性(如果它存在)(假设它是一个数组),并且对于该数组中的每个元素,调用 clone 函数以基于第二个模板单独克隆它它只是复制每个标签元素的ab属性。

If you are taking objects in and out of node and you want to control which attributes of those objects are cloned then this is a great way of controlling that in node.js and the code works in the browser too.如果您将对象进出节点,并且您想控制这些对象的哪些属性被克隆,那么这是在node.js中控制它的好方法,并且代码也可以在浏览器中运行。

Here is an example of it's use: http://jsfiddle.net/hjchyLt1/这是它的使用示例:http: //jsfiddle.net/hjchyLt1/

According to the Airbnb JavaScript Style Guide with 404 contributors:根据 404 位贡献者的Airbnb JavaScript 风格指南

Prefer the object spread operator over Object.assign to shallow-copy objects.将对象扩展运算符优于 Object.assign 用于浅拷贝对象。 Use the object rest operator to get a new object with certain properties omitted.使用 object rest 运算符获取省略某些属性的新对象。

// very bad
const original = { a: 1, b: 2 };
const copy = Object.assign(original, { c: 3 }); // this mutates `original` ಠ_ಠ
delete copy.a; // so does this

// bad
const original = { a: 1, b: 2 };
const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 }

// good
const original = { a: 1, b: 2 };
const copy = { ...original, c: 3 }; // copy => { a: 1, b: 2, c: 3 }

const { a, ...noA } = copy; // noA => { b: 2, c: 3 }

Also I'd like to warn you that even though Airbnb hardly recommends the object spread operator approach.另外我想警告您,即使 Airbnb 几乎不推荐对象传播运算符方法。 Keep in mind that Microsoft Edge still does not support this 2018 feature yet.请记住,Microsoft Edge 仍然不支持此 2018 功能。

ES2016+ Compat table >> ES2016+兼容表>>

Just as this link says use this code:如此链接所说,使用此代码:

let clone = Object.create(Object.getPrototypeOf(obj),
 Object.getOwnPropertyDescriptors(obj));

Simple简单的

var restore = { name:'charlesi',
age:9}
var prev_data ={
name: 'charles'
age : 10
}

var temp = JSON.stringify(prev_data)
restore = JSON.parse(temp)

restore = {
name:'charlie',
age : 12}

output prev_data:输出 prev_data:

{
name: 'charles'
age : 10
} 

In my code I frequently define a function (_) to handle copies so that I can pass by value to functions.在我的代码中,我经常定义一个function (_)来处理副本,以便我可以将by value传递给函数。 This code creates a deep copy but maintains inheritance.此代码创建一个深层副本,但保持继承。 It also keeps track of sub-copies so that self-referential objects can be copied without an infinite loop.它还跟踪子副本,以便可以在没有无限循环的情况下复制自引用对象。 Feel free to use it.随意使用它。

It might not be the most elegant, but it hasn't failed me yet.它可能不是最优雅的,但它还没有让我失望。

_ = function(oReferance) {
  var aReferances = new Array();
  var getPrototypeOf = function(oObject) {
    if(typeof(Object.getPrototypeOf)!=="undefined") return Object.getPrototypeOf(oObject);
    var oTest = new Object();
    if(typeof(oObject.__proto__)!=="undefined"&&typeof(oTest.__proto__)!=="undefined"&&oTest.__proto__===Object.prototype) return oObject.__proto__;
    if(typeof(oObject.constructor)!=="undefined"&&typeof(oTest.constructor)!=="undefined"&&oTest.constructor===Object&&typeof(oObject.constructor.prototype)!=="undefined") return oObject.constructor.prototype;
    return Object.prototype;
  };
  var recursiveCopy = function(oSource) {
    if(typeof(oSource)!=="object") return oSource;
    if(oSource===null) return null;
    for(var i=0;i<aReferances.length;i++) if(aReferances[i][0]===oSource) return aReferances[i][1];
    var Copy = new Function();
    Copy.prototype = getPrototypeOf(oSource);
    var oCopy = new Copy();
    aReferances.push([oSource,oCopy]);
    for(sPropertyName in oSource) if(oSource.hasOwnProperty(sPropertyName)) oCopy[sPropertyName] = recursiveCopy(oSource[sPropertyName]);
    return oCopy;
  };
  return recursiveCopy(oReferance);
};

// Examples:
Wigit = function(){};
Wigit.prototype.bInThePrototype = true;
A = new Wigit();
A.nCoolNumber = 7;
B = _(A);
B.nCoolNumber = 8; // A.nCoolNumber is still 7
B.bInThePrototype // true
B instanceof Wigit // true

You can use functional closure to gain all the benefits of a deep copy, without a deep copy.您可以使用功能闭包来获得深拷贝的所有好处,而无需深拷贝。 It's a very different paradigm, but works well.这是一个非常不同的范例,但效果很好。 Instead of trying to copy an existing object, just use a function to instantiate a new object when you need one.与其尝试复制现有对象,不如在需要时使用一个函数来实例化一个新对象。

First, create an function that returns an object首先,创建一个返回对象的函数

function template() {
  return {
    values: [1, 2, 3],
    nest: {x: {a: "a", b: "b"}, y: 100}
  };
}

Then create a simple shallow copy function然后创建一个简单的浅拷贝函数

function copy(a, b) {
  Object.keys(b).forEach(function(key) {
    a[key] = b[key];
  });
}

Create a new object, and copy the template's properties onto it创建一个新对象,并将模板的属性复制到它上面

var newObject = {}; 
copy(newObject, template());

But the above copy step is not necessary.但是上面的复制步骤不是必须的。 All you need to do is this:您需要做的就是:

var newObject = template();

Now that you have a new object, test to see what its properties are:现在你有了一个新对象,测试一下它的属性是什么:

console.log(Object.keys(newObject));

This displays:这显示:

["values", "nest"]

Yes, those are the newObject's own properties, not references to properties on another object.是的,这些是 newObject 自己的属性,而不是对另一个对象的属性的引用。 Let's just check:让我们检查一下:

console.log(newObject.nest.x.b);

This displays:这显示:

"b"

The newObject has acquired all of the template object's properties, but is free of any dependency chain. newObject 获得了模板对象的所有属性,但没有任何依赖链。

http://jsbin.com/ISUTIpoC/1/edit?js,console http://jsbin.com/ISUTIpoC/1/edit?js,console

I added this example to encourage some debate, so please add some comments :)我添加了这个例子来鼓励一些辩论,所以请添加一些评论:)

I think, that recurrence with caching is the best what we can do it here without libraries.我认为,在没有库的情况下,使用缓存重复是我们可以做到的最好的方法。

And underestimated WeakMap comes to the problem of cycles, wherein storing pairs of references to old and new object can help us to recreate pretty easily whole tree.而被低估的WeakMap会遇到循环问题,其中存储对新旧对象的引用对可以帮助我们轻松地重新创建整个树。

I prevented deep cloning of the DOM elements, probably you don't want to clone entire page :)我阻止了 DOM 元素的深度克隆,可能你不想克隆整个页面 :)

function deepCopy(object) {
    const cache = new WeakMap(); // Map of old - new references

    function copy(obj) {
        if (typeof obj !== 'object' ||
            obj === null ||
            obj instanceof HTMLElement
        )
            return obj; // primitive value or HTMLElement

        if (obj instanceof Date) 
            return new Date().setTime(obj.getTime());

        if (obj instanceof RegExp) 
            return new RegExp(obj.source, obj.flags);

        if (cache.has(obj)) 
            return cache.get(obj);

        const result = obj instanceof Array ? [] : {};

        cache.set(obj, result); // store reference to object before the recursive starts

        if (obj instanceof Array) {
            for(const o of obj) {
                 result.push(copy(o));
            }
            return result;
        }

        const keys = Object.keys(obj); 

        for (const key of keys)
            result[key] = copy(obj[key]);

        return result;
    }

    return copy(object);
}

Some tests:一些测试:

// #1
const obj1 = { };
const obj2 = { };
obj1.obj2 = obj2;
obj2.obj1 = obj1; // Trivial circular reference

var copy = deepCopy(obj1);
copy == obj1 // false
copy.obj2 === obj1.obj2 // false
copy.obj2.obj1.obj2 // and so on - no error (correctly cloned).

// #2
const obj = { x: 0 }
const clone = deepCopy({ a: obj, b: obj });
clone.a == clone.b // true

// #3
const arr = [];
arr[0] = arr; // A little bit weird but who cares
clone = deepCopy(arr)
clone == arr // false;
clone[0][0][0][0] == clone // true;

NOTE: I'm using constants, for of loop, => operator and WeakMaps to create more essential code.注意:我使用常量、for of 循环、=> 运算符和 WeakMaps 来创建更重要的代码。 This syntax (ES6) is supported by today's browsers今天的浏览器支持这种语法(ES6)

The solution JSON.parse(JSON.stringify(orig_obj) as stated by many peers here for deep_cloning has several issues which I found, and they are listed below:解决方案JSON.parse(JSON.stringify(orig_obj)正如许多同行所说的 deep_cloning 有几个我发现的问题,它们在下面列出:

  1. It discards the entries while copying whose values are undefined in the original object,它在复制原始对象中undefined的值时丢弃条目,
  2. If there are some values like Infinity , NaN etc, they will be converted into null while copying,如果有InfinityNaN等一些值,它们将在复制时转换为null
  3. If there is a Date type in the original object, it will be stringified in the cloned object ( typeof date_entry --> string ).如果原始对象中有Date类型,它将在克隆对象中进行字符串化( typeof date_entry --> string )。

Found an effective way for cloning an object, and it worked well for me in all sort of scenarios.找到了一种克隆对象的有效方法,它在各种情况下都对我很有效。 Please have a look at below code, as it has resolved all above mentioned pitfalls of JSON.parse(...) , yet resulting in proper deep-cloning:请看下面的代码,因为它解决了上面提到的JSON.parse(...)的所有缺陷,但导致正确的深度克隆:

var orig_obj = {
  string: 'my_str',
  number: 123,
  bool: false,
  nul: null,
  nested : {
    value : true
  },
  nan : NaN,
  date: new Date(), 
  undef: undefined,
  inf: Infinity,
}
console.log("original_obj before modification: ", orig_obj, "\n");
console.log(typeof orig_obj.date, "\n");

var clone_obj = Object.assign({}, orig_obj);

//this below loop will help in deep cloning and solving above issues
for(let prop in orig_obj) {
    if(typeof orig_obj[prop] === "object") {
        if(orig_obj[prop] instanceof Date)
            clone_obj[prop] = orig_obj[prop];
        else {
            clone_obj[prop] = JSON.parse(JSON.stringify(orig_obj[prop]));
        }
    }
}

console.log("cloned_obj before modification: ", orig_obj, "\n");

clone_obj.bool = true;
clone_obj.nested.value = "false";

console.log("original_obj post modification: ", orig_obj, "\n");
console.log("cloned_obj post modification: ", clone_obj, "\n");
console.log(typeof clone_obj.date);

I've tried this in the case of a scalar object and it works for me:我已经在标量对象的情况下尝试过这个,它对我有用:

function binder(i) {
  return function () {
    return i;
  };
}

a=1;
b=binder(a)(); // copy value of a into b

alert(++a); // 2
alert(b); // still 1

Regards.问候。

Here's a modern solution that doesn't have the pitfalls of Object.assign() (does not copy by reference):这是一个现代解决方案,它没有Object.assign()的缺陷(不通过引用复制):

const cloneObj = (obj) => {
    return Object.keys(obj).reduce((dolly, key) => {
        dolly[key] = (obj[key].constructor === Object) ?
            cloneObj(obj[key]) :
            obj[key];
        return dolly;
    }, {});
};

Using defaults (historically specific to nodejs but now usable from the browser thanks to modern JS):使用默认值(历史上特定于 nodejs,但现在可以从浏览器中使用,这要归功于现代 JS):

import defaults from 'object.defaults';

const myCopy = defaults({}, myObject);

To support a better understanding of copying of objects, this illustrative jsbin may be of value为了更好地理解对象的复制,这个说明性的 jsbin 可能很有价值

class base {
  get under(){return true}
}

class a extends base {}

const b = {
  get b1(){return true},
  b: true
}

console.log('Object assign')
let t1 = Object.create(b)
t1.x = true
const c = Object.assign(t1, new a())
console.log(c.b1 ? 'prop value copied': 'prop value gone')
console.log(c.x ? 'assigned value copied': 'assigned value gone')
console.log(c.under ? 'inheritance ok': 'inheritance gone')
console.log(c.b1 ? 'get value unchanged' : 'get value lost')
c.b1 = false
console.log(c.b1? 'get unchanged' : 'get lost')
console.log('-----------------------------------')
console.log('Object assign  - order swopped')
t1 = Object.create(b)
t1.x = true
const d = Object.assign(new a(), t1)
console.log(d.b1 ? 'prop value copied': 'prop value gone')
console.log(d.x ? 'assigned value copied': 'assigned value gone')
console.log(d.under ? 'inheritance n/a': 'inheritance gone')
console.log(d.b1 ? 'get value copied' : 'get value lost')
d.b1 = false
console.log(d.b1? 'get copied' : 'get lost')
console.log('-----------------------------------')
console.log('Spread operator')
t1 = Object.create(b)
t2 = new a()
t1.x = true
const e = { ...t1, ...t2 }
console.log(e.b1 ? 'prop value copied': 'prop value gone')
console.log(e.x ? 'assigned value copied': 'assigned value gone')
console.log(e.under ? 'inheritance ok': 'inheritance gone')
console.log(e.b1 ? 'get value copied' : 'get value lost')
e.b1 = false
console.log(e.b1? 'get copied' : 'get lost')
console.log('-----------------------------------')
console.log('Spread operator on getPrototypeOf')
t1 = Object.create(b)
t2 = new a()
t1.x = true
const e1 = { ...Object.getPrototypeOf(t1), ...Object.getPrototypeOf(t2) }
console.log(e1.b1 ? 'prop value copied': 'prop value gone')
console.log(e1.x ? 'assigned value copied': 'assigned value gone')
console.log(e1.under ? 'inheritance ok': 'inheritance gone')
console.log(e1.b1 ? 'get value copied' : 'get value lost')
e1.b1 = false
console.log(e1.b1? 'get copied' : 'get lost')
console.log('-----------------------------------')
console.log('keys, defineProperty, getOwnPropertyDescriptor')
f = Object.create(b)
t2 = new a()
f.x = 'a'
Object.keys(t2).forEach(key=> {
  Object.defineProperty(f,key,Object.getOwnPropertyDescriptor(t2, key))
})
console.log(f.b1 ? 'prop value copied': 'prop value gone')
console.log(f.x ? 'assigned value copied': 'assigned value gone')
console.log(f.under ? 'inheritance ok': 'inheritance gone')
console.log(f.b1 ? 'get value copied' : 'get value lost')
f.b1 = false
console.log(f.b1? 'get copied' : 'get lost')
console.log('-----------------------------------')
console.log('defineProperties, getOwnPropertyDescriptors')
let g = Object.create(b)
t2 = new a()
g.x = 'a'
Object.defineProperties(g,Object.getOwnPropertyDescriptors(t2))
console.log(g.b1 ? 'prop value copied': 'prop value gone')
console.log(g.x ? 'assigned value copied': 'assigned value gone')
console.log(g.under ? 'inheritance ok': 'inheritance gone')
console.log(g.b1 ? 'get value copied' : 'get value lost')
g.b1 = false
console.log(g.b1? 'get copied' : 'get lost')
console.log('-----------------------------------')

I have gone through all above solutions and they are quite well.我已经完成了上述所有解决方案,它们都很好。 However, there is another approach that you can use to clone object (with values not reference).但是,您可以使用另一种方法来克隆对象(使用值不引用)。 Object.assign 对象.assign

let x = {
    a: '1',
    b: '2'
}

let y = Object.assign({}, x)
y.a = "3"

console.log(x)

The output will be输出将是

{ a: '1', b: '2' }

Moreover, you can also clone array with the same approach.此外,您还可以使用相同的方法克隆数组。

clonedArray = Object.assign([], array)

Using the spread syntax performs a shallow copy of the object.使用扩展语法执行对象的浅拷贝。 This means that none of the nested object instances are cloned as you can see in the following example with the nested object child这意味着没有任何嵌套对象实例被克隆,正如您在下面的示例中看到的嵌套对象child对象一样

 const user1 = { name: 'Alex', address: '15th Park Avenue', age: 43, child:{ name: 'John' } } const user2 = {...user1}; user1.child.name = 'chris'; console.log(user1); console.log(user2);

To solve this nested object problem and perform a deep copy we can use JSON.parse(JSON.stringify(someObject))为了解决这个嵌套对象问题并执行深拷贝,我们可以使用JSON.parse(JSON.stringify(someObject))

 const user1 = { name: 'Alex', address: '15th Park Avenue', age: 43, child:{ name: 'John' } } const user2 = JSON.parse(JSON.stringify(user1)); user1.child.name = 'chris'; console.log(user1); console.log(user2);

Ok so this might be the very best option for shallow copying.好的,所以这可能是浅拷贝的最佳选择。 If follows the many examples using assign, but it also keeps the inheritance and prototype. if 遵循了许多使用 assign 的示例,但它也保留了继承和原型。 It's so simple too and works for most array-like and Objects except those with constructor requirements or read-only properties.它也很简单,适用于大多数类数组和对象,除了那些具有构造函数要求或只读属性的对象。 But that means it fails miserably for TypedArrays, RegExp, Date, Maps, Sets and Object versions of primitives (Boolean, String, etc..).但这意味着它对于 TypedArrays、RegExp、Date、Maps、Sets 和 Object 版本的原语(布尔值、字符串等)会惨遭失败。

function copy ( a ) { return Object.assign( new a.constructor, a ) }

Where a can be any Object or class constructed instance, but again not be reliable for thingies that use specialized getters and setters or have constructor requirements, but for more simple situations it rocks.其中a可以是任何 Object 或 class 构造的实例,但对于使用专门的 getter 和 setter 或具有构造函数要求的东西也不可靠,但对于更简单的情况,它会摇摆不定。 It does work on arguments as well.它也适用于论点。

You can also apply it to primitives to get strange results, but then... unless it just ends up being a useful hack, who cares.您也可以将它应用于原语以获得奇怪的结果,但是......除非它最终成为一个有用的黑客,谁在乎。

results from basic built-in Object and Array...结果来自基本的内置对象和数组...

> a = { a: 'A', b: 'B', c: 'C', d: 'D' }
{ a: 'A', b: 'B', c: 'C', d: 'D' }
> b = copy( a )
{ a: 'A', b: 'B', c: 'C', d: 'D' }
> a = [1,2,3,4]
[ 1, 2, 3, 4 ]
> b = copy( a )
[ 1, 2, 3, 4 ]

And fails because of mean get/setters, constructor required arguments or read-only properties, and sins against the father.并且由于平均 get/setter、构造函数所需的参数或只读属性以及对父亲的犯罪而失败。

> a = /\w+/g
/\w+/g
> b = copy( a )  // fails because source and flags are read-only
/(?:)/
> a = new Date ( '1/1/2001' )
2000-12-31T16:00:00.000Z
> b = copy( a )  // fails because Date using methods to get and set things
2017-02-04T14:44:13.990Z
> a = new Boolean( true )
[Boolean: true]
> b = copy( a )  // fails because of of sins against the father
[Boolean: false]
> a = new Number( 37 )
[Number: 37]
> b = copy( a )  // fails because of of sins against the father
[Number: 0]
> a = new String( 'four score and seven years ago our four fathers' )
[String: 'four score and seven years ago our four fathers']
> b = copy( a )  // fails because of of sins against the father
{ [String: ''] '0': 'f', '1': 'o', '2': 'u', '3': 'r', '4': ' ', '5': 's', '6': 'c', '7': 'o', '8': 'r', '9': 'e', '10': ' ', '11': 'a', '12': 'n', '13': 'd', '14': ' ', '15': 's', '16': 'e', '17': 'v', '18': 'e', '19': 'n', '20': ' ', '21': 'y', '22': 'e', '23': 'a', '24': 'r', '25': 's', '26': ' ', '27': 'a', '28': 'g', '29': 'o', '30': ' ', '31': 'o', '32': 'u', '33': 'r', '34': ' ', '35': 'f', '36': 'o', '37': 'u', '38': 'r', '39': ' ', '40': 'f', '41': 'a', '42': 't', '43': 'h', '44': 'e', '45': 'r', '46': 's' } 

I don't know which cases this doesn't work for, but it got me a copy of an array.我不知道这不适用于哪些情况,但它给了我一个数组的副本。 I think its cute :) Hope it helps我觉得它很可爱:) 希望它有帮助

copiedArr = origArr.filter(function(x){return true})

If your object is a class (eg https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes ):如果您的对象是一个类(例如https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes ):

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

Then in copiedObject you have a deep-copied instance of originalObject class with all its methods.然后在copiedObject你有一个originalObject对象类的深度复制实例及其所有方法。

If you are using TypeScript, need to support older web browsers (and so can't use Object.assign ), and aren't using a library with a clone method build in, you can make yourself a combine helper in a few lines of code.如果您使用的是 TypeScript,需要支持较旧的 Web 浏览器(因此不能使用Object.assign ),并且没有使用内置克隆方法的库,您可以在几行代码中让自己成为一个combine助手代码。 It combines objects, and if you have only one, just clones it.它结合了对象,如果你只有一个,就克隆它。

/** Creates a new object that combines the properties of the specified objects. */
function combine(...objs: {}[]) {
    const combined = {};
    objs.forEach(o => Object.keys(o).forEach(p => combined[p] = o[p]));
    return combined;
}

You may clone your Object without modification parent Object -您可以在不修改父对象的情况下克隆您的对象 -

    /** [Object Extend]*/
    ( typeof Object.extend === 'function' ? undefined : ( Object.extend = function ( destination, source ) {
        for ( var property in source )
            destination[property] = source[property];
        return destination;
    } ) );
    /** [/Object Extend]*/
    /** [Object clone]*/
    ( typeof Object.clone === 'function' ? undefined : ( Object.clone = function ( object ) {
        return this.extend( {}, object );
    } ) );
    /** [/Object clone]*/

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

    let clone = Object.clone( myObj );

    clone.a = 10;

    console.log('clone.a==>', clone.a); //==> 10

    console.log('myObj.a==>', myObj.a); //==> 1 // object not modified here

    let clone2 = Object.clone( clone );

    clone2.a = 20;

    console.log('clone2.a==>', clone2.a); //==> 20

    console.log('clone.a==>', clone.a); //==> 10 // object not modified here

I'm providing an answer to this question, because I do not see any native, recursive implementations here that resolve the problem of DOM elements.我正在为这个问题提供一个答案,因为我在这里看不到任何可以解决DOM元素问题的本机递归实现。

The problem there is that <element> has parent and child attributes, that link to other elements with parent and child values, which point back to the original <element> , causing either an infinite recursive or cyclic redundancy .问题在于<element>具有parent属性和child属性,它们链接到具有parent值和child值的其他元素,这些值指向原始<element> ,从而导致无限递归循环冗余

If your object is something safe and simple like如果您的对象是安全且简单的,例如

{
    '123':456
}

...then any other answer here will probably work. ...那么这里的任何其他答案都可能有效。

But if you have...但如果你有...

{
    '123':<reactJSComponent>,
    '456':document.createElement('div'),
}

...then you need something like this: ...那么你需要这样的东西:

    // cloneVariable() : Clone variable, return null for elements or components.
var cloneVariable = function (args) {
    const variable = args.variable;

    if(variable === null) {
            return null;
    }

    if(typeof(variable) === 'object') {
            if(variable instanceof HTMLElement || variable.nodeType > 0) {
                    return null;
            }

            if(Array.isArray(variable)) {
                    var arrayclone = [];

                    variable.forEach((element) => {
                            arrayclone.push(cloneVariable({'variable':element}));
                    });

                    return arrayclone;
            }

            var objectclone = {};

            Object.keys(variable).forEach((field) => {
                    objectclone[field] = cloneVariable({'variable':variable[field]});
            });

            return objectclone;
    }

    return variable;
}
var x = {'e': 2, 'd': 8, 'b': 5};

const y = {};
for(let key in x) {
    y[key] = x[key];
}
console.log(y); // =>>> {e: 2, d: 8, b: 5}

const z = {};
Object.keys(x).forEach(key => {
    z[key] = x[key];
});
console.log(z); // =>>> {e: 2, d: 8, b: 5}

const w = {};
for(let i = 0; i < Object.keys(x).length; i++) {
    w[Object.keys(x)[i]] = x[Object.keys(x)[i]];
}
console.log(w); // =>>> {e: 2, d: 8, b: 5}

const v = {};
for(let key of Object.keys(x)) {
    v[key] = x[key];
}
console.log(v); // =>>> {e: 2, d: 8, b: 5}

x['q'] = 100;   // Altering x will not affect the other objects

console.log(x); // =>>> {e: 2, d: 8, b: 5, q: 100}
console.log(y); // =>>> {e: 2, d: 8, b: 5}
console.log(z); // =>>> {e: 2, d: 8, b: 5}
console.log(w); // =>>> {e: 2, d: 8, b: 5}
console.log(v); // =>>> {e: 2, d: 8, b: 5}

Ways to Copy Objects in JavaScript在 JavaScript 中复制对象的方法

  1. Use the spread ( ... ) syntax使用展开 ( ... ) 语法
  2. Use the Object.assign() method使用Object.assign()方法
  3. Use the JSON.stringify() and JSON.parse() methods使用JSON.stringify()JSON.parse()方法
const person = {
    firstName: 'John',
    lastName: 'Doe'
};

// using spread ...
let p1 = {
    ...person
};

// using  Object.assign() method
let p2 = Object.assign({}, person);

// using JSON
let p3 = JSON.parse(JSON.stringify(person));

You can use rest operator to clone arrays or objects您可以使用 rest 运算符来克隆数组或对象

let myObj = {1: 100, 'a': 200};

let clone = {...myObj}; 

clone.a = 300;

console.log(clone.a) // Output :- 300
console.log(myObj.a) // Output :- 200

This makes new copy of your obj (not just reference).这会生成您的obj新副本(不仅仅是参考)。

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

..Works much efficiently then the _.cloneDeep(obj) . ..比_.cloneDeep(obj)更有效。

If there are no circular dependencies in your object, I suggest using one of the other answers or jQuery's copy methods , as they all seem quite effective.如果您的对象中没有循环依赖项,我建议使用其他答案之一或jQuery 的复制方法,因为它们看起来都非常有效。

If there are circular dependencies (ie, two sub-objects link to each other), you are kind of screwed as there is (from a theoretical perspective) no way to solve this issue elegantly .如果存在循环依赖关系(即两个子对象相互链接),那么您就有点搞砸了,因为(从理论上) 没有办法优雅地解决这个问题

//
// creates 'clone' method on context object
//
//  var 
//     clon = Object.clone( anyValue );
//
!((function (propertyName, definition) {
    this[propertyName] = definition();
}).call(
    Object,
    "clone",
    function () {
        function isfn(fn) {
            return typeof fn === "function";
        }

        function isobj(o) {
            return o === Object(o);
        }

        function isarray(o) {
            return Object.prototype.toString.call(o) === "[object Array]";
        }

        function fnclon(fn) {
            return function () {
                fn.apply(this, arguments);
            };
        }

        function owns(obj, p) {
            return obj.hasOwnProperty(p);
        }

        function isemptyobj(obj) {
            for (var p in obj) {
                return false;
            }
            return true;
        }

        function isObject(o) {
            return Object.prototype.toString.call(o) === "[object Object]";
        }
        return function (input) {
            if (isfn(input)) {
                return fnclon(input);
            } else if (isobj(input)) {
                var cloned = {};
                for (var p in input) {
                    owns(Object.prototype, p)
                    || (
                        isfn(input[p])
                        && ( cloned[p] = function () { return input[p].apply(input, arguments); } )
                        || ( cloned[p] = input[p] )
                    );
                }
                if (isarray(input)) {
                    cloned.length = input.length;
                    "concat every filter forEach indexOf join lastIndexOf map pop push reduce reduceRight reverse shift slice some sort splice toLocaleString toString unshift"
                    .split(" ")
                    .forEach(
                      function (methodName) {
                        isfn( Array.prototype[methodName] )
                        && (
                            cloned[methodName] =
                            function () {
                                return Array.prototype[methodName].apply(cloned, arguments);
                            }
                        );
                      }
                    );
                }
                return isemptyobj(cloned)
                       ? (
                          isObject(input)
                          ? cloned
                          : input
                        )
                       : cloned;
            } else {
                return input;
            }
        };
    }
));
//

I came to this page due to the same question but I'm neither using JQuery and none of the clone-Methods worked for my own objects.由于同样的问题,我来到了这个页面,但我既没有使用 JQuery,也没有一个克隆方法适用于我自己的对象。

I'm aware my answer isn't related too strong to this question because it's a different approach.我知道我的回答与这个问题并没有太大的关系,因为它是一种不同的方法。 Instead of using clone-functions I use a create function.我不使用克隆函数,而是使用创建函数。 It worked for me for the following (unfortunately restricting) purposes:它为以下(不幸地限制)目的对我有用:

  1. I use mostly JSP-generated Javascript我主要使用 JSP 生成的 Javascript
  2. I know in the beginning which Object must be generated (In my case it's Information from a Database which gets fetched once and needs to be deployed more often in the JS.我一开始就知道必须生成哪个对象(在我的情况下,它是来自数据库的信息,它被获取一次并且需要在 JS 中更频繁地部署。

First I defined my Objects like this:首先,我这样定义我的对象:

var obj= new Object();
obj.Type='Row';
obj.ID=1;
obj.Value='Blah blah';

Now I moved everything like:现在我移动了所有东西,比如:

function getObjSelektor(id_nummer,selected){
var obj = document.createElement("select");
obj.setAttribute("id","Selektor_"+id_nummer);
obj.setAttribute("name","Selektor");
obj.setAttribute("size","1");

var obj_opt_1 = document.createElement("option");
obj_opt_1.setAttribute("value","1");
if(1==selected)
    posopval_opt_1.setAttribute("selected","selected");
obj_opt_1.innerHTML="Blah blah";
obj.appendChild(obj_opt_1);

var obj_opt_2 = document.createElement("option");
obj_opt_2.setAttribute("value","2");
if(2==selected)
    obj_opt_2.setAttribute("selected","selected");
obj_opt_2.innerHTML="2nd Row";
obj.appendChild(obj_opt_2);

...

return obj;
}

And call the function in the regular code:并在常规代码中调用该函数:

myDiv.getObjSelektor(getObjSelektor(anotherObject.ID));

As said this is a different approach which solved my issue for my purposes.如前所述,这是一种不同的方法,它为我的目的解决了我的问题。

If you got an Object with Functions you can do it with JSONfn, see http://www.eslinstructor.net/jsonfn/ .如果你有一个带函数的对象,你可以用 JSONfn 来做,见http://www.eslinstructor.net/jsonfn/

var obj= {
    name:'Marvin',
    getName :  function(){
      return this.name;
    }
}
var cobj = JSONfn.parse(JSONfn.stringify(obj));

The problem with copying an object that, eventually, may point at itself, can be solved with a simple check.复制最终可能指向自身的对象的问题可以通过简单的检查来解决。 Add this check, every time there is a copy action.添加此检查,每次有复制操作。 It may be slow , but it should work.它可能很,但它应该可以工作。

I use a toType() function to return the object type, explicitly.我使用toType()函数显式地返回对象类型。 I also have my own copyObj() function, which is rather similar in logic, which answers all three Object(), Array(), and Date() cases.我也有自己的copyObj()函数,它在逻辑上非常相似,它回答了所有三个 Object()、Array() 和 Date() 情况。

I run it in NodeJS.我在 NodeJS 中运行它。

NOT TESTED, YET.尚未测试。

// Returns true, if one of the parent's children is the target.
// This is useful, for avoiding copyObj() through an infinite loop!
function isChild(target, parent) {
  if (toType(parent) == '[object Object]') {
    for (var name in parent) {
      var curProperty = parent[name];

      // Direct child.
      if (curProperty = target) return true;

      // Check if target is a child of this property, and so on, recursively.
      if (toType(curProperty) == '[object Object]' || toType(curProperty) == '[object Array]') {
        if (isChild(target, curProperty)) return true;
      }
    }
  } else if (toType(parent) == '[object Array]') {
    for (var i=0; i < parent.length; i++) {
      var curItem = parent[i];

      // Direct child.
      if (curItem = target) return true;

      // Check if target is a child of this property, and so on, recursively.
      if (toType(curItem) == '[object Object]' || toType(curItem) == '[object Array]') {
        if (isChild(target, curItem)) return true;
      }
    }
  }

  return false;     // Not the target.
}

To handle circular objects that that JSON.stringify can't handle, you can bring in a library called JSOG , that serializes and deserializes arbitrary graphs into JSON format.要处理JSON.stringify无法处理的循环对象,您可以引入一个名为JSOG的库,它将任意图形序列化和反序列化为 JSON 格式。

var clone = JSOG.parse(JSOG.stringify(original));

It might also be interesting to try patching JSOG for cloning with this trick (don't have time at the moment, but if someone wants to give it a shot...):尝试用这个技巧修补 JSOG 以进行克隆也可能很有趣(目前没有时间,但如果有人想试一试......):

Serialize a simple function :序列化一个简单的函数:

foo.f = function(a) { return a }
var stringForm = foo.f.toString() // "function (a) { return a }"

Deserialize a function :反序列化一个函数:

eval("foo.f = " + stringForm)

Some conventions (probably in the name of the property) to identify functions vs regular strings would be needed ( @func_f perhaps).需要一些约定(可能以属性的名称)来识别函数与常规字符串(可能是@func_f )。

Of course if the function calls a second function the second function will need to exist just as it would for the original.当然,如果该函数调用第二个函数,则第二个函数将需要像原来的那样存在。

The above however is quite dangerous if you are to accept the serialized form from an untrusted source, but then accepting any function in any form from an untrusted source would be dangerous, so if you're interested in cloning functions trust must have already been established (or you're already intent on writing a security flaw!).但是,如果您要接受来自不受信任来源的序列化形式,上述内容是非常危险的,但是接受来自不受信任来源的任何形式的任何函数都是危险的,因此如果您对克隆函数感兴趣,则必须已经建立信任(或者您已经打算编写安全漏洞!)。

Disclaimer: I have not tested the speed of JSOG stringify/parse vs JSON stringify/parse, but It does work on the simple (circular) objects I tested it with.免责声明:我没有测试过 JSOG stringify/parse vs JSON stringify/parse 的速度,但它确实适用于我测试过的简单(圆形)对象。

Ok, I know it has many answers, but no one pointed out, EcmaScript5 has assign method, work on FF and Chrome, it copies enumerable and own properties and Symbols.好的,我知道它有很多答案,但没有人指出,EcmaScript5 有分配方法,适用于 FF 和 Chrome,它复制可枚举和自己的属性和符号。

Object Assign 对象分配

所以只是添加一些简单的东西,如果你想创建一个灯箱或类似的东西,上面的大多数答案都是不必要的复杂,简单地复制源属性和其他任何必要的东西会更容易。

Short and sweet:简短而甜蜜:

let clone = Object.fromEntries(Object.entries(obj));

Demo:演示:

 let obj = {a: 'b'}; let clone = Object.fromEntries(Object.entries(obj)); clone.a = 'c'; console.log(obj, clone);

The different不同的

Only copy top level: {...object} and Object.assign({}, object)仅复制顶层: {...object}Object.assign({}, object)

 let objA = { a: "keyA", b: { c: "keyC", } } let objB = Object.assign({}, objA); // or {...objB} // change objB objB.a = "Change objA.a (top)" console.log("objA.a (top) No Change:\n" + JSON.stringify(objA, false, 2)); objB.bc = "change should be only for objB.bc but it in objA.bc" console.log("objA.ac second level has Change:\n" + JSON.stringify(objA, false, 2));

for deep copy use structuredClone() 2022 or JSON.parse(JSON.stringify(object)) for old browser, easy without hack.对于深拷贝,对旧浏览器使用structuredClone() 2022 或JSON.parse(JSON.stringify(object)) ,无需破解即可轻松使用。

 let objA = { a: "keyA", b: { c: "keyC", } } let objB = typeof structuredClone == 'function' ? structuredClone(objA) : JSON.parse(JSON.stringify(objA)); // change objB objB.a = "Change objA.a (top)" objB.bc = "change should be only for objB.c but it in objA.c" console.log("objA has no Change:\n" + JSON.stringify(objA, false, 2));

From the Apple JavaScript Coding Guidelines :来自Apple JavaScript 编码指南

// Create an inner object with a variable x whose default
// value is 3.
function innerObj()
{
        this.x = 3;
}
innerObj.prototype.clone = function() {
    var temp = new innerObj();
    for (myvar in this) {
        // this object does not contain any objects, so
        // use the lightweight copy code.
        temp[myvar] = this[myvar];
    }
    return temp;
}

// Create an outer object with a variable y whose default
// value is 77.
function outerObj()
{
        // The outer object contains an inner object.  Allocate it here.
        this.inner = new innerObj();
        this.y = 77;
}
outerObj.prototype.clone = function() {
    var temp = new outerObj();
    for (myvar in this) {
        if (this[myvar].clone) {
            // This variable contains an object with a
            // clone operator.  Call it to create a copy.
            temp[myvar] = this[myvar].clone();
        } else {
            // This variable contains a scalar value,
            // a string value, or an object with no
            // clone function.  Assign it directly.
            temp[myvar] = this[myvar];
        }
    }
    return temp;
}

// Allocate an outer object and assign non-default values to variables in
// both the outer and inner objects.
outer = new outerObj;
outer.inner.x = 4;
outer.y = 16;

// Clone the outer object (which, in turn, clones the inner object).
newouter = outer.clone();

// Verify that both values were copied.
alert('inner x is '+newouter.inner.x); // prints 4
alert('y is '+newouter.y); // prints 16

Steve史蒂夫

I've had an issue when copying objects.我在复制对象时遇到了问题。 This is because when you do following, you're only making a 'reference' to the object and when the source object value is updated later, the copy object that was cloned also changes value because it was merely a 'reference' and hence you see multiple values of the last changes to the source object.这是因为当您执行以下操作时,您只是对对象进行“引用”,并且稍后更新源对象值时,被克隆的复制对象也会更改值,因为它只是一个“引用”,因此您查看源对象最后一次更改的多个值。

let x = { a: 1 };
let y = x; // y is a reference to x, so if x changes y also changes and v/v

So, to tackle this issue you do the following:因此,要解决此问题,请执行以下操作:

let y = JSON.parse(JSON.stringify(x)); //see Note below

The other way to prevent references is by doing the following:防止引用的另一种方法是执行以下操作:

let x = { a: 1 };
let y = Object.assign({}, x); // Object.assign(target, ...sources)

y.a = 2;
console.log(x); // { a: 1 }
console.log(y); // { a: 2 }

Note: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#warning_for_deep_clone注意: https ://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#warning_for_deep_clone

I found a way to clone object with functions (break multiple lines to easier understand):我找到了一种使用函数克隆 object 的方法(分多行以便于理解):

const clone = Object.assign(
    Object.create(
        Object.getPrototypeOf(originalObject)
    ),
    dataObject
);

My favorite & elegant JS objects clone solution is我最喜欢和优雅的 JS 对象克隆解决方案是

function CloneObject() {}
function cloneObject(o) {
   CloneObject.prototype = o;
   return new CloneObject();
}

Use cloneObject(object) to get a clone of JS object.使用cloneObject(object)获取 JS 对象的克隆。

Unlike many copy solutions this clone keeps prototype relationship in cloned object.与许多复制解决方案不同,此克隆将原型关系保留在克隆对象中。

function clone(obj)
{
    var cloneObj = Object.create(obj);

    return cloneObj;
}

In Javascript objects individually inherit another object (Prototypal inheritance).在 Javascript 中对象单独继承另一个对象(原型继承)。 Object.create(obj) returns an object that is a sub-object or child object of obj. Object.create(obj) 返回一个对象,它是 obj 的子对象或子对象。 In the above function it will effectively return a copy of the object.在上述函数中,它将有效地返回对象的副本。

However, This is a very odd way to clone because I am not using inheritance for its real purpose.但是,这是一种非常奇怪的克隆方式,因为我没有将继承用于其真正目的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM