我有一个对象x。我想将其复制为对象y,这样对y的更改不会修改x。我意识到复制从内置JavaScript对象派生的对象会导致额外的、不需要的财产。这不是问题,因为我正在复制我自己的一个文字构造对象。

如何正确克隆JavaScript对象?


当前回答

2020年7月6日更新

有三(3)种方法可以在JavaScript中克隆对象。由于JavaScript中的对象是引用值,因此不能简单地使用=进行复制。

方法如下:

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

这可以用作参考摘要。

其他回答

如果您有一个带有函数的对象,可以使用JSONfn来实现,请参见http://www.eslinstructor.net/jsonfn/.

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

您可以克隆一个对象,并使用一行代码从上一个对象中删除任何引用。只需执行以下操作:

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

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

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

对于当前不支持Object.create的浏览器/引擎,可以使用以下polyfill:

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

您可以在不修改父对象的情况下克隆对象-

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

本文来自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;
};

好的,这可能是浅层复制的最佳选择。If遵循了许多使用赋值的示例,但它也保留了继承和原型。它也很简单,适用于大多数类数组和对象,但有构造函数要求或只读财产的对象除外。但这意味着它对于TypedArrays、RegExp、Date、Maps、Set和Object版本的原语(Boolean、String等)失败得很惨。

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

其中a可以是任何Object或类构造的实例,但对于使用专门的getter和setter或具有构造函数要求的对象来说,它同样不可靠,但对于更简单的情况来说,它很难。它也能处理争论。

您也可以将其应用于原语以获得奇怪的结果,但是。。。除非它最终成为有用的黑客,谁在乎呢。

基本内置对象和数组的结果。。。

> 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 ]

由于平均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' }