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

如何正确克隆JavaScript对象?


当前回答

const objClone = { ...obj };

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

其他回答

本文来自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;
};
let clone = Object.assign( Object.create( Object.getPrototypeOf(obj)), obj)

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

表演

今天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对于浅大型对象的示例结果

使用jQuery,可以使用extend进行浅层复制:

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

对copiedObject的后续更改不会影响originalObject,反之亦然。

或制作深度副本:

var copiedObject = jQuery.extend(true, {}, originalObject)
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}