这是在 ES6 中克隆对象的好方法吗?

Is this a good way to clone an object in ES6?

谷歌搜索 "javascript clone object" 会带来一些非常奇怪的结果,其中一些已经过时了,而另一些则太复杂了,是不是就这么简单:

let clone = {...original};

这有什么问题吗?

编辑:发布此答案时,{...obj} 语法在大多数浏览器中不可用。现在,你应该可以很好地使用它(除非你需要支持 IE 11)。

使用Object.assign。

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

var obj = { a: 1 };
var copy = Object.assign({}, obj);
console.log(copy); // { a: 1 }

但是,这不会产生深度克隆。目前还没有原生的深度克隆方法。

编辑:正如@Mike 'Pomax' Kamermans 在评论中提到的那样,您可以使用 JSON.parse(JSON.stringify(input))

深度克隆简单对象(即没有原型、函数或循环引用)

这对于浅层克隆很好object spread is a standard part of ECMAScript 2018.

对于深度克隆,您需要 different solution.

const clone = {...original} 到浅层克隆

const newobj = {...original, prop: newOne} 将另一个道具不变地添加到原始道具并存储为新对象。

如果您不想使用 json.parse(json.stringify(object)) 您可以创建递归键值副本:

function copy(item){
  let result = null;
  if(!item) return result;
  if(Array.isArray(item)){
    result = [];
    item.forEach(element=>{
      result.push(copy(element));
    });
  }
  else if(item instanceof Object && !(item instanceof Function)){ 
    result = {};
    for(let key in item){
      if(key){
        result[key] = copy(item[key]);
      }
    }
  }
  return result || item;
}

但最好的方法是创建一个 class 可以 return 自我克隆

class MyClass{
    data = null;
    constructor(values){ this.data = values }
    toString(){ console.log("MyClass: "+this.data.toString(;) }
    remove(id){ this.data = data.filter(d=>d.id!==id) }
    clone(){ return new MyClass(this.data) }
}
We can do that with two way:
1- First create a new object and replicate the structure of the existing one by iterating 
 over its properties and copying them on the primitive level.

let user = {
     name: "John",
     age: 30
    };

    let clone = {}; // the new empty object

    // let's copy all user properties into it
    for (let key in user) {
      clone[key] = user[key];
    }

    // now clone is a fully independant clone
    clone.name = "Pete"; // changed the data in it

    alert( user.name ); // still John in the original object

2- Second we can use the method Object.assign for that 
    let user = { name: "John" };
    let permissions1 = { canView: true };
    let permissions2 = { canEdit: true };

    // copies all properties from permissions1 and permissions2 into user
    Object.assign(user, permissions1, permissions2);

  -Another example

    let user = {
      name: "John",
      age: 30
    };

    let clone = Object.assign({}, user);
It copies all properties of user into the empty object and returns it. Actually, the same as the loop, but shorter.

但是 Object.assign() 没有创建深度克隆

let user = {
  name: "John",
  sizes: {
    height: 182,
    width: 50
  }
};

let clone = Object.assign({}, user);

alert( user.sizes === clone.sizes ); // true, same object

// user and clone share sizes
user.sizes.width++;       // change a property from one place
alert(clone.sizes.width); // 51, see the result from the other one

为了解决这个问题,我们应该使用克隆循环来检查 user[key] 的每个值,如果它是一个对象,那么也复制它的结构。这就是所谓的“深度克隆”。

有一种深度克隆的标准算法可以处理上述情况和更复杂的情况,称为结构化 cloning algorithm。 为了不重新发明轮子,我们可以使用 JavaScript 库 lodash the method is called _.cloneDeep(obj).

中的有效实现

如果您使用的方法不适用于涉及 Date 等数据类型的对象,试试这个

导入_

import * as _ from 'lodash';

深度克隆对象

myObjCopy = _.cloneDeep(myObj);

根据@marcel 的回答,我发现克隆对象上仍然缺少一些功能。例如

function MyObject() {
  var methodAValue = null,
      methodBValue = null

  Object.defineProperty(this, "methodA", {
    get: function() { return methodAValue; },
    set: function(value) {
      methodAValue = value || {};
    },
    enumerable: true
  });

  Object.defineProperty(this, "methodB", {
    get: function() { return methodAValue; },
    set: function(value) {
      methodAValue = value || {};
    }
  });
}

在 MyObject 上我可以克隆 methodA 但 methodB 被排除在外的地方。发生这种情况是因为缺少

enumerable: true

这意味着它没有出现在

for(let key in item)

我切换到

Object.getOwnPropertyNames(item).forEach((key) => {
    ....
  });

这将包括不可枚举的键。

我还发现原型(proto)没有被克隆。为此,我最终使用了

if (obj.__proto__) {
  copy.__proto__ = Object.assign(Object.create(Object.getPrototypeOf(obj)), obj);
}

PS:令人沮丧的是我找不到内置函数来执行此操作。

以上所有方法均不处理嵌套到 n 级的对象的深度克隆。我没有检查它的性能,但它很简短。

下面的第一个示例显示了使用 Object.assign 克隆到第一级的对象克隆。

var person = {
    name:'saksham',
    age:22,
    skills: {
        lang:'javascript',
        experience:5
    }
}

newPerson = Object.assign({},person);
newPerson.skills.lang = 'angular';
console.log(newPerson.skills.lang); //logs Angular

使用下面的方法深度克隆对象

var person = {
    name:'saksham',
    age:22,
    skills: {
        lang:'javascript',
        experience:5
    }
}

anotherNewPerson = JSON.parse(JSON.stringify(person));
anotherNewPerson.skills.lang = 'angular';
console.log(person.skills.lang); //logs javascript

你也可以这样做,

let copiedData = JSON.parse(JSON.stringify(data));

我找到了一个似乎也可以复制函数的解决方案,如果此示例有误,请纠正我。

注意,我还没有用更复杂的对象案例测试过这个方法,例如,将包括带有 this 的方法以供参考

以早餐的价格为例,我在全球都有这个价格,但我想针对酒店房间单独调整它

// make an object for a booking option
var opt_resa = { breakfast_val: 900 }

// i define a function for opt_resa : 
opt_resa.func = function(){ alert('i am a function'); }

// copy object in modif.opt_resa :
var modif = { opt_resa : {} }

for ( var v in opt_resa ){

    modif.opt_resa[v] = opt_resa[v];
}

// test
modif.opt_resa.breakfast_val = 1500;

// old value
console.log( opt_resa.breakfast_val );
// output : 900

// modified value
console.log( modif.opt_resa.breakfast_val );
// output : 1500

// function copied
modif.opt_resa.func(); 
// this function works

结构化克隆 你可以使用这个方法

function Copy_Object(obj) { return structuredClone(obj); }