我有一个对象x。我想将其复制为对象y,这样对y的更改不会修改x。我意识到复制从内置JavaScript对象派生的对象会导致额外的、不需要的财产。这不是问题,因为我正在复制我自己的一个文字构造对象。
如何正确克隆JavaScript对象?
我有一个对象x。我想将其复制为对象y,这样对y的更改不会修改x。我意识到复制从内置JavaScript对象派生的对象会导致额外的、不需要的财产。这不是问题,因为我正在复制我自己的一个文字构造对象。
如何正确克隆JavaScript对象?
当前回答
您可以克隆一个对象,并使用一行代码从上一个对象中删除任何引用。只需执行以下操作:
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();
};
}
其他回答
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;
}
您可以在不修改父对象的情况下克隆对象-
/** [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
表演
今天2020.04.30我在MacOs High Sierra v10.13.6上对Chrome v81.0、Safari v13.1和Firefox v75.0上选择的解决方案进行了测试。
我专注于复制DATA(具有简单类型字段的对象,而不是方法等)的速度。解决方案A-I只能进行浅层复制,解决方案J-U可以进行深层复制。
浅层副本的结果
解决方案{…obj}(A)在chrome和firefox上速度最快,在safari上速度中等基于Object.assign(B)的解决方案在所有浏览器上都很快jQuery(E)和lodash(F,G,H)解决方案是中等/相当快的解决方案JSON.parse/stringify(K)非常慢解决方案D和U在所有浏览器上都很慢
深度复制的结果
解决方案Q在所有浏览器上都是最快的jQuery(L)和lodash(J)是中速的解决方案JSON.parse/stringify(K)非常慢解决方案U在所有浏览器上都是最慢的lodash(J)和1000级深对象的Chrome上的U崩溃解决方案
细节
对于选择的解决方案:A.BC(我的)DEFGH我JKLMNOPQRSTU我进行了4次测试
浅小:具有10个非嵌套字段的对象-可以在此处运行浅大:具有1000个非嵌套字段的对象-可以在此处运行deep-small:具有10级嵌套字段的对象-可以在此处运行deep-big:具有1000级嵌套字段的对象-可以在此处运行
测试中使用的对象显示在下面的代码段中
let obj_ShallowSmall={字段0:假,字段1:真,字段2:1,字段3:0,字段4:空,字段5:[],字段6:{},field7:“text7”,字段8:“text8”,}让obj_DepSmall={级别0:{级别1:{级别2:{级别3:{级别4:{级别5:{级别6:{第7级:{级别8:{第9级:[[[[[[[[]],}}}}}}}}},};让obj_ShallowBig=数组(1000).fill(0).reduce((a,c,i)=>(a['field'+i]=getField(i),a),{});让obj_DepBig=genDeepObject(1000);// ------------------//显示对象// ------------------console.log('obj_ShallowSmall:',JSON.stringify(obj_SharlowSmall));console.log('obj_DepSmall:',JSON.stringify(obj_DeepSmall));console.log('obj_ShallowBig:',JSON.stringify(obj_SharlowBig));console.log('obj_DepBig:',JSON.stringify(obj_DeepBig));// ------------------//助手// ------------------函数getField(k){设i=k%10;如果(i==0)返回false;如果(i==1)返回真;如果(i==2)返回k;如果(i==3)返回0;如果(i==4)返回null;如果(i==5)返回[];如果(i==6)返回{};如果(i>=7)返回“text”+k;}函数genDeepObject(N){//生成:{level0:{level1:{…levelN:{end:[[[…N次…['abc']…]]}}}}…}}让obj={};设o=obj;设arr=[];设a=arr;for(设i=0;i<N;i++){o['level'+i]={};o=o['level'+i];设aa=[];a.推(aa);a=aa;}a[0]=“公元前”;o['end']=arr;返回obj;}
下面的代码片段展示了经过测试的解决方案,并显示了它们之间的差异
函数A(obj){返回{…obj}}函数B(obj){return Object.assign({},obj);}函数C(obj){return Object.keys(obj).reduce((a,c)=>(a[c]=obj[c],a),{})}函数D(obj){让copyOfObject={};Object.defineProperties(copyOfObject,Object.getOwnPropertyDescriptors(obj));返回copyOfObject;}函数E(obj){return jQuery.exextend({},obj)//浅}函数F(obj){return _.clone(obj);}函数G(obj){return _.clone(obj,true);}函数H(obj){返回扩展({},obj);}函数I(obj){如果(null==obj||“对象”!=obj类型)返回obj;var copy=obj.constructor();for(obj中的var属性){如果(obj.hasOwnProperty(attr))copy[attr]=obj[attr];}返回副本;}函数J(obj){return _.cloneDeep(obj,true);}函数K(obj){返回JSON.parse(JSON.stringify(obj));}函数L(obj){return jQuery.exextend(true,{},obj)//deep}函数M(obj){如果(obj==null | |类型(obj)!='对象')返回obj;var temp=新建obj.constructor();for(obj中的var键)temp[key]=M(obj[key]);返回温度;}函数N(obj){设EClone=函数(obj){var newObj=(数组的obj实例)?[] : {};for(对象中的变量i){如果(i=='EClone')继续;如果(obj[i]&&类型obj[i]==“对象”){newObj[i]=EClone(obj[i]);}否则newObj[i]=obj[i]}返回newObj;};返回EClone(obj);};函数O(obj){如果(obj==null||typeof obj!=“object”)返回obj;如果(obj.constructor!=对象&&obj.constructionr!=数组)返回obj;如果(obj.constructor==日期| | obj.constructionr==RegExp | | obc.constructor==函数||obj.structor==字符串||obj.structor==数字||obc.structor==布尔值)返回新的obj.constructor(obj);let to=new obj.constructor();for(obj中的var名称){to[name]=typeof to[name]==“undefined”?O(obj[名称],null):到[名称];}返回;}函数P(obj){函数克隆(目标,源){for(输入源代码){//使用getOwnPropertyDescriptor而不是source[key]来防止触发setter/getter。let descriptor=Object.getOwnPropertyDescriptor(源,键);if(字符串的descriptor.value实例){target[key]=新字符串(descriptor.value);}else if(数组的descriptor.value实例){target[key]=克隆([],descriptor.value);}else if(descriptor.value对象实例){let prototype=Reflect.getPrototypeOf(descriptor.value);let cloneObject=clone({},descriptor.value);Reflect.setPrototypeOf(克隆对象,原型);target[key]=克隆对象;}其他{Object.defineProperty(目标、键、描述符);}}let prototype=Reflect.getPrototypeOf(源代码);Reflect.setPrototypeOf(目标,原型);回归目标;}返回克隆({},obj);}函数Q(obj){var复制;//处理3个简单类型,以及null或undefined如果(null==obj||“对象”!=obj类型)返回obj;//处理日期if(日期的obj实例){copy=新日期();copy.setTime(obj.getTime());返回副本;}//句柄数组if(数组的obj实例){copy=[];对于(变量i=0,len=obj.length;i<len;i++){copy[i]=Q(obj[i]);}返回副本;}//句柄对象if(对象的obj实例){copy={};for(obj中的var属性){如果(obj.hasOwnProperty(attr))copy[attr]=Q(obj.[attr]);}返回副本;}throw new Error(“无法复制obj!不支持其类型。”);}函数R(obj){const gdcc=“__getDeepCircularCopy__”;if(obj!==对象(obj)){返回obj;//原始值}var set=obj中的gdcc,缓存=obj[gdcc],后果if(设置缓存类型==“函数”){return cache();}//其他obj[gdcc]=函数(){return result;};//覆盖if(数组的obj实例){结果=[];对于(var i=0;i<obj.length;i++){结果[i]=R(obj[i]);}}其他{结果={};for(obj中的var属性)if(prop!=gdcc)result[prop]=R(obj[prop]);else if(设置)result[prop]=R(缓存);}if(设置){obj[gdcc]=缓存;//重置}其他{删除对象[gdcc];//再次取消设置}返回结果;}函数S(obj){const cache=new WeakMap();//新旧参考地图函数副本(对象){if(对象类型!=='对象'||对象==空||HTMLElement的对象实例)返回对象;//原始值或HTMLElementif(日期的对象实例)return new Date().setTime(object.getTime());if(RegExp的对象实例)返回新的RegExp(object.source,object.flags);if(cache.has(对象))返回cache.get(对象);const result=数组的对象实例?[] : {};cache.set(对象,结果);//在递归开始之前存储对对象的引用if(数组的对象实例){for(对象的常量){result.push(复制(o));}返回结果;}const keys=对象.keys(对象);for(键的常量键)result[key]=复制(对象[key]);返回结果;}返回副本(obj);}
下面是Chrome对于浅大型对象的示例结果
我已经完成了上述所有解决方案,它们都很好。然而,您可以使用另一种方法来克隆对象(值不引用)。对象分配
let x = {
a: '1',
b: '2'
}
let y = Object.assign({}, x)
y.a = "3"
console.log(x)
输出将为
{ a: '1', b: '2' }
此外,您还可以使用相同的方法克隆阵列。
clonedArray = Object.assign([], array)
对于深度复制和克隆,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}}