这是在 ES6 中克隆对象的好方法吗?
Posted
技术标签:
【中文标题】这是在 ES6 中克隆对象的好方法吗?【英文标题】:Is this a good way to clone an object in ES6? 【发布时间】:2017-02-05 18:40:33 【问题描述】:谷歌搜索“javascript clone object”会带来一些非常奇怪的结果,其中一些已经过时了,而另一些则太复杂了,这不是那么简单吗:
let clone = ...original;
这有什么问题吗?
【问题讨论】:
这不是合法的 ES6。但如果是,这不是克隆:您的克隆和原始属性现在都指向相同的东西。例如,original = a: [1,2,3]
给你一个克隆,clone.a
字面意思是original.a
。通过clone
或original
进行修改会修改相同的东西,所以不,这很糟糕=)
@AlbertoRivera 这是有点有效的 JavaScript,因为它是一个 stage 2 提案,可能会成为 JavaScript 标准的未来补充。
@Frxstrem 的问题是关于 ES6,这不是有效的 JavaScript =)
浅克隆还是深层克隆?
你是对的,它不是有效的 ES6,它是 有效的 ES9。 developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
【参考方案1】:
这对浅层克隆很有用。 object spread is a standard part of ECMAScript 2018。
对于深度克隆,您需要different solution。
const clone = ...original
浅克隆
const newobj = ...original, prop: newOne
不变地将另一个道具添加到原始道具并存储为新对象。
【讨论】:
但是,这不只是一个浅克隆吗?如,属性不是递归克隆的,是吗?因此, original.innerObject === clone.innerObject 和更改 original.innerObject.property 将更改 clone.innerObject.property。 是的,这是一个浅克隆。如果你想要一个深度克隆,你必须使用JSON.parse(JSON.stringify(input))
/!\ JSON.parse(JSON.stringify(input)) 弄乱了日期,未定义,... 这不是克隆的灵丹妙药!见:maxpou.fr/immutability-js-without-library
那么 hack JSON.stringify()/JSON.parse() 真的是在 ES6 中深度克隆对象的推荐方法吗?我一直看到它推荐。令人不安。
@MarkShust JSON.parse(JSON.stringify(input))
将不起作用,因为如果有 functions
或 infinity
作为值,它将简单地分配 null
代替它们的位置。只有在简单的值是literals
而不是functions
时,它才会起作用。【参考方案2】:
编辑:发布此答案时,...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 在 cmets 中提到的,您可以使用 JSON.parse(JSON.stringify(input))
深度克隆简单对象(即没有原型、函数或循环引用)
【讨论】:
有一个,前提是您的对象是真正的对象字面量和纯数据,在这种情况下,JSON.parse(JSON.stringify(input))
是一个适当的深度克隆。但是,一旦原型、函数或循环引用发挥作用,该解决方案就不再有效。
@Mike'Pomax'Kamermans 这是真的。但是,失去 getter 和 setter 的功能是可怕的......
如果您需要通用函数来深度克隆任何对象,请查看***.com/a/13333781/560114。
现在有办法做到deep cloning natively。
@DanDascalescu 尽管它是实验性的,但它看起来很有希望。感谢您的信息!【参考方案3】:
如果您使用的方法不适用于涉及 Date 等数据类型的对象,请试试这个
导入_
import * as _ from 'lodash';
深度克隆对象
myObjCopy = _.cloneDeep(myObj);
【讨论】:
只需import _ from 'lodash';
就足够了。但是 +1 表示“不要重新发明***”的答案。
lodash 臃肿。真的不需要为了一个简单的深拷贝而引入 lodash。这里有很多其他解决方案。对于希望构建精益应用的 Web 开发人员来说,这是一个非常糟糕的答案。
Webpack tree-shaking 是 Jason 的解决方案。您也可以只导入该函数:npmjs.com/package/lodash.clonedeep。 +1 使用已知良好的解决方案而不是重新发明***【参考方案4】:
如果你不想使用 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 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)
【讨论】:
【参考方案5】:你也可以这样做,
let copiedData = JSON.parse(JSON.stringify(data));
【讨论】:
这会起作用,但是对象的数据类型变成了字符串 :( 例如,当使用 stringify 时,日期对象变成了一个具有转换值的字符串【参考方案6】:根据@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:令人沮丧的是我找不到内置函数来执行此操作。
【讨论】:
【参考方案7】: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 中的工作实现,该方法称为 _.cloneDeep(obj)。
【讨论】:
【参考方案8】:我找到了一个似乎也复制函数的解决方案,如果此示例有错误,请纠正我。
注意我没有用更复杂的对象案例测试这个方法,例如,它会包含带有 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] = $.o.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
【讨论】:
【参考方案9】:上述所有方法都不能处理嵌套到 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
【讨论】:
JSON.parse/stringify 被认为是 years 的一种糟糕的深度克隆方法。请检查以前的答案以及相关问题。此外,这对 ES6 来说并不新鲜。 @DanDascalescu 我知道这一点,我认为将它用于简单对象应该不是问题。其他人也在同一篇文章的回答中提到了这一点,甚至作为 cmets。我认为它不值得一票否决。 完全正确-“其他人在他们的答案中也提到了”JSON.parse/stringify。为什么要使用相同的解决方案发布另一个答案?以上是关于这是在 ES6 中克隆对象的好方法吗?的主要内容,如果未能解决你的问题,请参考以下文章