-
35 请参阅此问题: stackoverflow.com/questions/122102/… – 北部尼亚兹 评论 2011年6月21日10:13 -
291 对于JSON,我使用 mObj=JSON.parse(JSON.stringify(jsonObject)); – Loh勋爵。 评论 2013年2月2日10:09 -
80 我真的不明白为什么没有人建议 对象.create(o) ,它能满足作者的要求吗? – 冷冻 评论 2014年8月8日15:23 -
60 varx={deep:{key:1}}; var y=对象创建(x); x.deep.key=2; 完成此操作后, y.dep.key键 也将是2,因此Object.create不能用于克隆。。。 – 鲁本·斯托克 评论 2015年7月4日15:04 -
23 @r3wt不起作用……请在对溶液进行基本测试后发布。。 – 用户3275211 评论 2016年2月16日18:54
82个答案
2022年更新
const clone=structuredClone(对象);
老答案
功能克隆(obj){ 如果(null==obj||“object”!=typeofobj)返回obj; var copy=对象构造函数(); for(obj中的var属性){ 如果(obj.hasOwnProperty(attr))copy[attr]=obj[attr]; } 返回副本; } var d1=新日期(); /*5秒后执行功能*/ setTimeout(函数(){ var d2=克隆(d1); 警报(“d1=”+d1.toString()+“\nd2=”+d2.toString()); }, 5000);
功能克隆(obj){ var复制; //处理3个简单类型,null或未定义 如果(null==obj||“object”!=typeofobj)返回obj; //处理日期 if(obj实例日期){ copy=新日期(); copy.setTime(obj.getTime()); 返回副本; } //句柄数组 if(obj实例数组){ copy=[]; 对于(var i=0,len=obj.length;i<len;i++){ copy[i]=克隆(obj[i]); } 返回副本; } //处理对象 if(obj对象实例){ copy={}; for(obj中的var属性){ 如果(obj.hasOwnProperty(attr))copy[attr]=克隆(obj[attr]); } 返回副本; } throw new Error(“无法复制obj!不支持其类型。”); }
//这将是可克隆的: var树={ “left”:{“left“:null,”right“:null,”data“:3}, “right”:空, “数据”:8 }; //这有点管用,但你会得到两份 //内部节点,而不是对同一副本的2个引用 var directedAcylicGraph={ “left”:{“left”:null,“right”:null,“data”:3}, “数据”:8 }; directedAcyclicGraph[“right”]=directedAcyclicGraph[“left”]; //克隆它会由于无限递归而导致堆栈溢出: var cyclicGraph={ “left”:{“left“:null,”right“:null,”data“:3}, “数据”:8 }; cyclicGraph[“right”]=cyclicGraph;
-
-
-
2 -
2 -
1 在移动设备上使用此功能时遇到问题…- iOS 11,12,13,14,不适用于Safari和Google Chrome。 (适用于iOS 15、16)-安卓12、11、10、9,适用于三星互联网浏览器(chrome适用)。 – jfx忍者 评论 2022年9月21日14:49
常数a={ string:'字符串', 编号:123, bool:错误, nul:空, date:new date(),//字符串化 undef:未定义,//丢失 inf:无限,//强制为“null” } 控制台.log(a); console.log(日期类型);// Date对象 const clone=JSON.parse(JSON.stringify(a)); console.log(克隆); console.log(clone.date类型);// .toISOString()的结果
-
1 -
这很有用,但在比较包含其他对象的对象时,当两个完全相等的对象不相等时,我会遇到意外的行为。 使用了JSON.stringify(x)==JSON.strongify。 – 阿古斯汀·拉库拉 评论 2022年4月28日13:36 -
-
三
一种在一行代码中克隆Javascript对象的优雅方法
var clone=Object.assign({},obj);
assign()方法用于将所有可枚举自身属性的值从一个或多个源对象复制到目标对象。
if(!Object.assign){ Object.defineProperty(Object,'assign'{ 可枚举:false, 可配置:true, 可写:true, 值:函数(目标){ '使用严格'; if(目标===未定义||目标===null){ throw new TypeError(“无法将第一个参数转换为对象”); } var to=对象(目标); for(var i=1;i<参数长度;i++){ var nextSource=参数[i]; if(nextSource===未定义||nextSource===空){ 继续; } nextSource=对象(nextSource); var keysArray=Object.keys(nextSource); for(var nextIndex=0,len=keysArray.length;nextIndex<len;next Index++){ var nextKey=keysArray[nextIndex]; var desc=Object.getOwnPropertyDescriptor(nextSource,nextKey); if(desc!==未定义&&desc.enumerable){ to[nextKey]=nextSource[nexdKey]; } } } 返回; } }); }
-
57 -
这种继承在哪里有用(呸!) 源对象不会被修改,因此这两个对象之间的关系没有问题。
变量foo={a:1}; var bar=Object.create(foo); foo.a;// 1 bar.a;// 1 foo.a=2; bar.a;// 2-原型已更改 bar.a=3; foo.a;// 仍然是2,因为设置bar.a使其成为“自己的”属性
启动情况
函数Circ(){ this.me=这个; } 函数嵌套(y){ this.y=y; }
变量a={ x: “a”, circ:new circ(), 嵌套:new nested('a') };
变量b=a; b.x=“b”; b.嵌套。y=“b”;
控制台.log(a,b); -->对象{ x: “b”, circ:圆形{ 我:圆形{…} }, 嵌套:嵌套{ y: “b” } } b-->对象{ x: “b”, circ:圆形{ 我:圆形{…} }, 嵌套:嵌套{ y: “b” } }
JSON格式
var b=JSON.parse(JSON.stringify(a)); b.x=“b”; b.nested.y=“b”;
递归副本 (公认的“答案”)
函数cloneSO(obj){ //处理3个简单类型,null或未定义 如果(null==obj||“object”!=typeofobj)返回obj; //处理日期 if(obj实例日期){ var copy=新日期(); copy.setTime(obj.getTime()); 返回副本; } //句柄数组 if(obj实例数组){ var副本=[]; for(var i=0,len=obj.length;i<len;i++){ copy[i]=克隆SO(obj[i]); } 返回副本; } //句柄对象 if(obj对象实例){ var-copy={}; for(obj中的var属性){ 如果(obj.hasOwnProperty(attr))copy[attr]=cloneSO(obj[attr]); } 返回副本; } throw new Error(“无法复制obj!不支持其类型。”); }
var b=克隆SO(a); b.x=“b”; b.nested.y=“b”;
本机解决方案
var b=对象创建(a); b.x=“b”; b.nested.y=“b”;
控制台.log(a,b); -->对象{ x: “a”, circ:圆形{ 我:圆形{…} }, 嵌套:嵌套{ y: “b” } } b-->对象{ x: “b”, circ:圆形{ 我:Circ{…} }, 嵌套:嵌套{ y: “b” } }
天然溶液用聚乙烯填料
函数F(){}; 函数克隆PF(o){ F.原型=o; 返回新的F(); } var b=克隆PF(a); b.x=“b”; b.nested.y=“b”;
控制台.log(a,b); -->对象{ x: “a”, circ:圆形{ 我:圆形{…} }, 嵌套:嵌套{ y: “b” } } b-->F{ x: “b”, circ:圆形{ 我:圆形{…} }, 嵌套:嵌套{ y: “b” } } 控制台.log(类型a,类型b); -->对象 b-->对象 console.log(对象的一个实例,对象的b个实例); a-->正确 b-->真 console.log(F的一个实例,F的b个实例); a-->假 b-->正确
更好(但不是完美)的解决方案
功能克隆DR(o){ const gdcc=“__getDeepCircularCopy__”; if(o!==对象(o)){ 返回o;// 原始值 } var集合=o中的gdcc, 缓存=o[gdcc], 结果; if(set&&typeofcache==“函数”){ return cache(); } //其他 o[gdcc]=function(){返回结果;};// 覆盖 if(o数组实例){ 结果=[]; 对于(var i=0;i<o.length;i++){ 结果[i]=克隆DR(o[i]); } }其他{ 结果={}; for(o中的var属性) if(prop!=gdcc) 结果[prop]=克隆DR(o[prop]); else if(设置) result[prop]=克隆DR(缓存); } if(设置){ o[gdcc]=缓存;// 重置 }其他{ 删除o[gdcc];// 再次取消设置 } 返回结果; } var b=克隆DR(a); b.x=“b”; b.嵌套。y=“b”;
控制台.log(a,b); -->对象{ x: “a”, circ:对象{ me:对象{…} }, 嵌套:对象{ y: “a” } } b-->对象{ x: “b”, circ:对象{ me:对象{…} }, 嵌套:对象{ y: “b” } } 控制台.log(类型a,类型b); -->对象 b-->对象 console.log(对象的实例,对象的实例); a-->正确 b-->正确 console.log(F的一个实例,F的另一个实例); a-->假 b-->假
共享一片叶子的树的结构不会被复制,它们将成为两片独立的叶子:
[对象][对象] / \ / \ / \ / \ |/_ _\| |/_ _\| [对象][对象]===>[对象][Object] \ / | | \ / | | _\| |/_ \|/ \|/ [对象][对象][Object]
结论
-
为了快速开始使用lodash,我建议学习npm、Browserify和lodash。 我让clone使用“npm I--save lodash.clone”,然后使用“var clone=require('lodash.colone');” 为了满足工作需要,您需要类似browserify的东西。 一旦你安装了它并了解了它的工作原理,你将使用“browserify yourfile.js>bundle.js; 每次运行代码时启动chromeindex.html(而不是直接进入chrome)。 这会将您的文件和您需要的所有文件从npm模块收集到bundle.js中。 不过,使用Gulp可能可以节省时间并自动执行此步骤。 – 用户11104582 评论 2019年4月9日19:43
设obj={a:1,b:2,c:3}// 欧洲标准6
varobj={a:1,b:2,c:3}// 欧洲标准5
let cloned=Object.assign({},obj)// 新{a:1,b:2,c:3};
让克隆={…obj}// 新{a:1,b:2,c:3};
let cloned=JSON.parse(JSON.stringify(obj)); //新{a:1,b:2,c:3};, 可以很方便,但避免反复使用大块数据
2020年7月6日更新
const food={食物:‘苹果’,饮料:‘牛奶’} // 1. 使用“排列” // ------------------ {…食物} // 2. 使用“Object.assign” // ------------------ 对象.assign({},食物) // 3. “JSON” // ------------------ JSON.parse(JSON.stringify(食物)) //结果: //{食物:苹果,饮料:牛奶}
-
-
10 -
三 -
1 -
三 对于具有层次结构的对象,即嵌套对象,扩展运算符和Object.assign失败。 JSON.parse/stringify可以工作,但如上所述,它不会复制方法。 – i代码 评论 2021年3月31日17:40
var对象1={键:“值”}; var对象2=对象1; object2=JSON.stringify(object1); object2=JSON.parse(object2); object2.key=“更改”; console.log(对象1);// 返回值
-
-
6 函数不是JSON规范的一部分,因为它们不是一种安全(或智能)的数据传输方式,而JSON正是为此而设计的。 我知道Firefox中的原生JSON编码器只是忽略了传递给它的函数,但我不确定其他人的行为。 – 克里斯·沃克 评论 2009年10月30日10:27 -
1 -
设x={a:'value1'} 设x2={…x} //=>在没有引用的情况下进行变异: x2.a=“值2” console.log(x.a)//=>“value1”
常量y={a:{b:'value3'}} 常数y2={…y} //=>嵌套对象仍然是引用: y2.a.b=“值4” console.log(y.a.b)//=>“值4”
-
1 -
4 -
const objClone={…obj};
-
2 -
-
-
三 @SunilGarg要复制嵌套属性,也可以使用 const objDeepClone=JSON.parse(JSON.stringify(obj)); – 帕万·加雷 评论 2019年9月6日13:45
Object.prototype.clone=函数(){ var newObj=(此数组实例)? [] : {}; for(此处为var i){ 如果(i==“克隆”)继续; if(此[i]&&此[i]的类型==“对象”){ newObj[i]=此[i].clone(); }else newObj[i]=此[i] }return newObj; };
-
4 -
-
三 @iPadDeveloper2011上面的代码中有一个错误,它创建了一个名为“i”的全局变量(用于此中的i),而不是“(用于此的var i)”。 我有足够的业力来编辑和修复它,所以我做到了。 – 米凯马卡纳 评论 2012年9月22日22:09 -
2 -
三
功能克隆(obj){ if(obj==null||typeof(obj)!=' 对象') 返回对象; var temp=新对象构造函数(); for(obj中的var键) temp[key]=克隆(obj[key]); 返回温度; }
-
11 这个答案很接近,但不太正确。 如果尝试克隆Date对象,则不会获得相同的日期,因为调用Date构造函数会使用当前日期/时间初始化新的Date。 该值不可枚举,也不会被for/in循环复制。 – A.利维 评论 2009年4月8日4:21 -
-
-
-
Object.prototype.clone=函数(){ if(this.cloneNode)返回this.cloeNode(true); var copy=这个数组实例? [] : {}; for(此处为var-attr){ if(this[attr]==“function”|this[attr]==null||!this[Atttr].clone的类型) copy[attr]=此[attr]; 否则,如果(this[attr]==this)copy[attr]=copy; else copy[attr]=这个[attr].clone(); } 返回副本; } Date.prototype.clone=函数(){ var copy=新日期(); copy.setTime(this.getTime()); 返回副本; } 编号.原型.克隆= 布尔型.原型.克隆= String.prototype.clone=函数(){ 返回此; }
性能
浅层复制结果
解决方案 {…对象} (A) 在chrome和firefox上速度最快,在safari上速度中等 解决方案基于 对象分配 (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)和Chrome上1000级深目标的解决方案U崩溃
细节
shallow-small:具有10个非嵌套字段的对象-您可以运行它 酒店雇员和饭馆雇员 sharew-big:具有1000个非嵌套字段的对象-您可以运行它 酒店雇员和饭馆雇员 deep-small:具有10层嵌套字段的对象-您可以运行它 酒店雇员和饭馆雇员 deep-big:具有1000级嵌套字段的对象-您可以运行它 酒店雇员和饭馆雇员
设obj_ShallowSmall={ 字段0:假, 字段1:真, 字段2:1, 字段3:0, 字段4:空, 字段5:[], 字段6:{}, 字段7:“text7”, field8:“文本8”, } 让obj_DeepSmall={ 级别0:{ 级别1:{ 第2级:{ 第3级:{ 级别4:{ 第5级:{ 级别6:{ 第7级:{ 第8级:{ 第9级:[[[[abc]]]]], }}}}}}}}}, }; 让obj_ShallowBig=Array(1000).fill(0).reduce((a,c,i)=>(a['field'+i]=getField(i),a),{}); 让obj_DeepBig=genDeepObject(1000); // ------------------ //显示对象 // ------------------ console.log('obj_ShallowSmall:',JSON.stringify(obj_ShollowSmall)); console.log('obj_DepSmall:',JSON.stringify(obj_DeepSmall)); console.log('obj_ShallowBig:',JSON.stringify(obj_ShollowBig)); console.log('obj_DeepBig:',JSON.stringify(obj_DepBig)); // ------------------ //帮助 // ------------------ 函数getField(k){ 设i=k%10; 如果(i==0)返回false; 如果(i==1)返回true; 如果(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['水平'+i]; 设aa=[]; a.推动(aa); a=aa; } a[0]='abc'; o['end']=arr; 返回对象; }
功能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)); return copyOfObject; } 功能E(obj){ return jQuery.extend({},obj)//浅层 } 函数F(obj){ 返回克隆(obj); } 函数G(obj){ 返回克隆(obj,true); } 函数H(obj){ return_.extend({},obj); } 函数I(对象){ if(null==obj||“object”!=typeof obj)返回obj; var copy=对象构造函数(); for(obj中的var属性){ 如果(obj.hasOwnProperty(attr))copy[attr]=obj[attr]; } 返回副本; } 函数J(obj){ return _.cloneDeep(obj,true); } 函数K(obj){ return JSON.parse(JSON.stringify(obj)); } 函数L(obj){ return jQuery.extend(true,{},obj)//深度 } 函数M(obj){ if(obj==null||typeof(obj)!=' 对象') 返回对象; var temp=新对象构造函数(); for(obj中的var键) 温度[键]=M(对象[键]); 返回温度; } 函数N(obj){ 让EClone=函数(obj){ var newObj=(数组的obj实例)? [] : {}; for(obj中的var i){ 如果(i==“EClone”)继续; if(obj[i]&&对象类型[i]==“对象”){ newObj[i]=克隆(对象[i]); }else newObj[i]=对象[i] }return newObj; }; return EClone(obj); }; 函数O(obj){ 如果(obj==null||typeofobj!=“object”)返回obj; if(obj.constructor!=对象&&obj.constructor!=数组)返回obj; if(obj.constructor==日期||obj.confructor==RegExp||obj.construtor==函数|| obj.constructor==字符串||obj.constructor==数字||obj.constructionor==布尔值) 返回新的obj.constructor(obj); let to=新obj.constructor(); for(对象中的变量名称) { to[name]=类型to[name]=“未定义”? O(obj[name],null):到[name]; } 返回; } 函数P(obj){ 功能克隆(目标、源){ for(让键入源代码){ //使用getOwnPropertyDescriptor而不是source[key]来防止触发setter/getter。 let描述符=Object.getOwnPropertyDescriptor(源,键); if(字符串的描述符.value实例){ target[key]=新字符串(descriptor.value); } else if(数组的描述符.value实例){ target[key]=克隆([],descriptor.value); } else if(对象的描述符.value实例){ let prototype=Reflect.getPrototypeOf(descriptor.value); 让cloneObject=克隆({},descriptor.value); Reflect.setPrototypeOf(cloneObject,prototype); target[key]=克隆对象; } 其他{ Object.defineProperty(目标、键、描述符); } } let prototype=Reflect.getPrototypeOf(源); Reflect.setPrototypeOf(目标,原型); 返回目标; } 返回克隆({},obj); } 函数Q(obj){ var复制; //处理3个简单类型,null或未定义 如果(null==obj||“object”!=typeofobj)返回obj; //处理日期 if(obj实例日期){ copy=新日期(); copy.setTime(obj.getTime()); 返回副本; } //句柄数组 if(obj实例数组){ copy=[]; 对于(var i=0,len=obj.length;i<len;i++){ copy[i]=Q(obj[i]); } 返回副本; } //句柄对象 if(对象的obj实例){ 副本={}; for(obj中的var属性){ 如果(obj.hasOwnProperty(attr))copy[attr]=Q(obj[attr]); } 返回副本; } throw new Error(“无法复制obj!不支持其类型。”); } 功能R(对象){ const gdcc=“__getDeepCircularCopy__”; if(obj!==对象(obj)){ 返回对象;// 原始值 } var集合=对象中的gdcc, 缓存=obj[gdcc], 结果; if(set&&typeofcache==“函数”){ return cache(); } //其他 obj[gdcc]=function(){返回结果;};// 覆盖 if(obj实例数组){ 结果=[]; for(var i=0;i<obj.length;i++){ 结果[i]=R(对象[i]); } }其他{ 结果={}; for(obj中的var属性) if(prop!=gdcc) 结果[prop]=R(obj[prop]); else if(设置) 结果[prop]=R(缓存); } if(设置){ obj[gdcc]=缓存;// 重置 }其他{ 删除对象[gdcc];// 再次复位 } 返回结果; } 函数S(obj){ const缓存=新WeakMap();// 新旧参考地图 函数副本(对象){ if(typeof object!==“object”|| 对象===空|| HTMLElement的对象实例 ) 返回对象;// 原语值或HTMLElement if(对象实例日期) return new Date().setTime(object.getTime()); if(RegExp的对象实例) return new RegExp(object.source,object.flags); if(cache.has(对象)) return-cache.get(对象); const result=数组的对象实例? [] : {}; cache.set(对象,结果);// 在递归开始之前存储对对象的引用 if(数组的对象实例){ for(对象常数){ 结果推送(复制(o)); } 返回结果; } const keys=Object.keys(对象); for(键的常量键) 结果[key]=副本(对象[key]); 返回结果; } 返回副本(obj); } 功能T(obj){ var clonedObjectsArray=[]; var originalObjectsArray=[]// 用于在完成时删除唯一ID var next_objid=0; 函数objectId(obj){ 如果(obj==null)返回null; if(obj.__obj_id==未定义){ obj.__obj_id=下一个objid++; 原始对象数组[对象.__obj_id]=对象j; } 返回对象__obj_id; } 函数cloneRecursive(obj){ 如果(null==obj||typeofobj==“string”||typeof obj===“number”||typeof obj==“boolean”)返回obj; //处理日期 if(obj实例日期){ var copy=新日期(); copy.setTime(obj.getTime()); 返回副本; } //手柄阵列 if(obj实例数组){ var副本=[]; 对于(var i=0;i<obj.length;++i){ copy[i]=克隆递归(obj[i]); } 返回副本; } //处理对象 if(obj对象实例){ if(clonedObjectsArray[对象ID(obj)]!= 未定义) return clonedObjectsArray[对象ID(obj)]; var复制; if(obj instanceof Function)//句柄函数 copy=function(){return obj.apply(this,arguments);}; 其他的 copy={}; clonedObjectsArray[objectId(obj)]=复制; for(obj中的var属性) if(属性!=“__obj_id”&&obj.hasOwnProperty(属性)) copy[attr]=克隆递归(obj[attr]); 返回副本; } throw new Error(“无法复制obj!不支持其类型。”); } var cloneObj=克隆递归(obj); //删除唯一的id for(var i=0;i<originalObjectsArray.length;i++) { 删除原始对象阵列[i]__ 对象id; }; return cloneObj; } 功能U(obj){ /* 按值而不是按引用深度复制对象, 异常:`代理` */ const seen=新WeakMap() 返回克隆(obj) 函数defineProp(object,key,descriptor={},copyFrom={}){ 常量{configurable:configurable,writable:writable} =Object.getOwnPropertyDescriptor(对象,键) ||{可配置:true,可写:true} const test=_configuratable//可以重新定义属性 &&(_writeable==未定义||_writeable)//可以分配给属性 if(!test | | arguments.length<=2)返回测试 const basisDesc=对象.getOwnPropertyDescriptor(copyFrom,key) ||{configurable:true,writable:true}//自定义… || {}; // … 或保留为本机默认设置 [“get”,“set”,“value”,“writable”,“enumerable”,“configurable”] .forEach(属性=> 描述符[attr]===未定义&& (描述符[attr]=basisDesc[attr]) ) 常量{get,set,value,writable,enumerable,configurable} =描述符 return Object.defineProperty(对象,键{ 可枚举、可配置。。。 获取集合 ? {get,set}//访问器描述符 :{value,writable}//数据描述符 }) } 函数克隆(对象){ if(object!==object(object))返回对象/* --检查对象是否属于基元数据类型*/ if(节点的对象实例)返回object.cloneNode(true)/* --克隆DOM树*/ let _ object//对象的克隆 开关(object.constructor){ case数组: case对象: _object=克隆对象(对象) 打破 案例日期: _object=新日期(+object) 打破 case函数: const fnStr=字符串(对象) _object=新函数(“return”+ (/^(?!函数|[^{]+?=>)[^(]+?\(/.test(fnStr)) ? “函数”:“” )+fnStr )() copyPropDescs(_object,object) 打破 案例RegExp: _object=新RegExp(对象) 打破 违约: switch(Object.prototype.toString.call(Object.constructor)){ ////词干来源: case“[object Function]”://`class` case“[object Undefined]”://`object.create(null)` _object=克隆对象(对象) 打破 default://`代理` _object=对象 } } 返回对象(_O) } 函数cloneObject(对象){ if(seen.has(object))返回seen.get(objects)/* --处理递归引用(循环结构)*/ const_object=数组.isArray(对象) ? [] :Object.create(Object.getPrototypeOf(Object))/* --分配[[原型]]进行继承*/ seen.set(对象,对象)/* --使`_object`成为`object`的关联镜像*/ Reflect.ownKeys(object).forEach(key=>) defineProp(_object,key,{value:克隆(object[key])},object) ) 返回对象(_O) } 函数copyPropDescs(目标,源){ Object.defineProperties(目标, Object.getOwnPropertyDescriptors(源) ) } } // ------------------------ //测试属性 // ------------------------ console.log(`shallow-deph func circ未定义日期RegExp bigInt`) 对数(A); 对数(B); 对数(C); 对数(D); 对数(E); 对数(F); 对数(G); 对数(H); 日志(I); 对数(J); log(K); 对数(L); 对数(M); 对数(N); 对数(O); 对数(P); 对数(Q); 对数(R); 对数(S); 对数(T); 对数(U); console.log(`shallow deep func circ未定义日期RegExp bigInt ---- 图例: 浅层-解决方案创建浅层副本 deep-解决方案创建deep拷贝 func-解决方案复制函数 circ解决方案可以复制具有循环引用的对象 undefined-具有未定义值的解决方案复制字段 日期-解决方案可以复制日期 RegExp-解决方案可以使用正则表达式复制字段 bigInt-解决方案可以复制bigInt `) // ------------------------ //助手函数 // ------------------------ 函数deepCompare(obj1,obj2){ return JSON.stringify(obj1)===JSON.stringify(obj2); } 函数getCase(){//纯数据案例 返回{ undef:未定义, bool:true,num:1,str:“txt1”, e1:null,e2:[],e3:{},e4:0,e5:false, arr:[false,2,“txt3”,空,[],{}, [真,4,“txt5”,空,[],{},[真,6,“txt 7”,空, {bool:true,num:8,str:“txt9”,e1:null,e2:[],e3:{},e4:0,e5:false} ], {bool:true,num:10,str:“txt11”,e1:null,e2:[],e3:{},e4:0,e5:false} ], 对象:{ bool:true,num:12,str:“txt13”, e1:null,e2:[],e3:{},e4:0,e5:false, arr:[true,14,“txt15”,null,[],{}], 对象:{ bool:true,num:16,str:“txt17”, e1:null,e2:[],e3:{},e4:0,e5:false, arr:[真,18,“txt19”,空,[],{}], obj:{bool:true,num:20,str:“txt21”,e1:null,e2:[],e3:{},e4:0,e5:false} } } }; } 功能检查(组织、副本、字段、newValue){ copy[field]=newValue; return deepCompare(org,copy); } 功能测试Func(f){ 设o={a:1,有趣:(i,j)=>i+j}; 设c=f(o); 让val=假 尝试{ val=c.fun(3,4)==7; }捕获(e){} 返回值; } 功能测试Circ(f){ 函数Circ(){ this.me=这个; } var o={ x: “a”, circ:new circ(), obj_circ:空, }; o.obj_circ=o; 设val=false; 尝试{ 设c=f(o); val=(o.obj_circ==o)&&(o.circ==o.circ.me); }捕获(e){} 返回值; } 功能测试RegExp(f){ 设o={ 回复:/a[0-9]+/, }; 设val=false; 尝试{ 设c=f(o); val=(字符串(c.re)==字符串(/a[0-9]+/)); }捕获(e){} 返回val; } 功能测试日期(f){ 设o={ date:new date(), }; 设val=false; 尝试{ 设c=f(o); val=(+new Date(c.Date)==+new日期(o.Date)); }捕获(e){} 返回值; } 功能测试BigInt(f){ 设val=false; 尝试{ 设o={ 大:123n, }; 设c=f(o); val=o.big==c.big; }捕获(e){} 返回值; } 函数日志(f){ 设o=getCase();// 原始对象 让oB=getCase();// “备份”用于浅层有效测试 设c1=f(o);// 副本1供参考 设c2=f(o);// 副本2用于测试浅层值 设c3=f(o);// 副本3用于测试深度值 让is_proper_copy=深度比较(c1,o);// 应该是真的 //浅层变化 让testShallow= [['bool',false],['num',666],[['str','xyz'],[arr',[]],[`obj',{}]] .reduce((acc,curr)=>acc&&检查(c1,c2,curr[0],curr[1]),true); //应为true(原始对象不应更改浅域) 让is_valid=deepCompare(o,oB); //深度测试(引入一些变化) 如果(c3.arr[6])c3.arr[6][7].num=777; 让diff_shallow=! 测试浅层;// 应该是真的(复制了浅域) 让diff_deep=! 深度比较(c1,c3);// 应该是真的(深场被复制) let can_copy_functions=测试函数(f); 让can_copy_circular=testCirc(f); 让can_copy_regexp=testRegExp(f); 让can_copy_date=测试日期(f); 让can_copy_bigInt=测试bigInt(f); 让has_undefined=c1中的'undef';// 复制具有未定义值的字段? 设is_ok=is_valid&&is_proper_copy; 设b=(bool)=>(bool+'').padEnd(5,'');// 将布尔值转换为格式化字符串 testFunc(f); if(is_ok){ 控制台.log }其他{ 控制台.log(`${f.name}:INVALID${is_valid}${is_proper_copy}`,{c1}) } }
<script src=“ https://code.jquery.com/jquery-3.5.0.min.js网址 “integrity=”sha256-xNzN2a4ltkB44Mc/Jz3pT4iU1cmeR0FkXs4pru/JxaQ=“crossorigin=”anonymous“></script> <script src=“ https://cdn.jsdeliver.net/npm网址/ [电子邮件保护] /lodash.min.js“></script> 这段代码只展示了经过测试的解决方案,并显示了它们之间的差异(但它没有进行性能测试)
让obj={person:'Thor Odinson'}; let clone=Object.assign({},obj);
-
12 -
-
@忍住饥饿你错了。 在浏览器的JS控制台中进行检查( 让obj={person:'Thor Odinson'}; let clone=Object.assign({},obj); clone.title=“Whazup”; ) – 坍塌的 评论 2017年9月1日11:00 -
@collapser:这正是我检查的内容,然后console.log(person)将是“Whazzup”,而不是“Thor Odinson”。 请参阅8月份的评论。 – 抑制饥饿 评论 2017年9月1日11:40 -
1 @Chrome 60.0.3112.113和Edge 14.14393中不存在HoldOffHunger; August的注释不适用于 对象 的属性确实被克隆了。 对象本身的属性值不会被克隆。 – 拼贴画 评论 2017年9月1日12:50
var obj1={text:'moo1'}; var obj2=对象创建(obj1);// 创建不带引用的新克隆 obj2.text='moo2';// 仅更新obj2的文本属性 控制台.log(obj1,obj2);// 输出:obj1:{text:'moo1'},obj2:{text:`moo2'}
//Polyfill Object.create(如果不存在) if(!Object.create){ Object.create=函数(o){ var F=函数(){}; F.原型=o; 返回new F(); }; }
-
1 -
-
-
12 -
2
keepMeTheSame={第一个:“我!”,第二个:“你!”}; 克隆={…keepMeTheSame}
-
2 -
据我所知,spread操作符只适用于迭代器- 开发人员.mozilla.org 说: var obj={“key1”:“value1”}; var数组=[…obj];// TypeError:obj不可迭代 – 奥勒 评论 2017年4月4日8:12 -
-
@manikantgautam我以前使用Object.assign(),但现在最新的Chrome、Firefox(仍然不支持Edge和Safari)支持对象传播语法。 其ECMAScript提案。。。 但据我所知,巴别塔确实支持它,所以使用它可能是安全的。 – 奥勒 评论 2017年12月6日15:11
保持属性相互独立。 并在克隆对象上保持方法的活性。
let deepCloned=JSON.parse(JSON.stringify(source)); let merged=Object.assign({},source); Object.assign(合并,deepCloned);
功能
函数克隆(对象){ /* 按值而不是按引用深度复制对象, 异常:`代理` */ const seen=新WeakMap() 返回克隆(对象) 函数克隆(对象){ if(object!==object(object))返回对象/* --检查对象是否属于基元数据类型*/ if(节点的对象实例)返回object.cloneNode(true)/* --克隆DOM树*/ let _object//对象的克隆 开关(object.constructor){ case数组: case对象: _object=克隆对象(对象) 打破 案例日期: _object=新日期(+object) 打破 case函数: _object=copyFn(对象) 打破 案例RegExp: _object=新RegExp(对象) 打破 违约: switch(Object.prototype.toString.call(Object.constructor)){ ////词干来源: 案例“[object Function]”: switch(对象[Symbol.toStringTag]){ 大小写未定义: _object=克隆对象(object)//`类` 打破 案例“AsyncFunction”: “发电机功能”案例: 案例“AsyncGeneratorFunction”: _object=copyFn(对象) 打破 违约: _object=对象 } 打破 case“[object Undefined]”://`object.create(null)` _object=克隆对象(对象) 打破 违约: _object=object//`代理` } } 返回对象(_O) } 函数cloneObject(对象){ if(seen.has(object))返回seen.get(objects)/* --处理递归引用(循环结构)*/ const_object=数组.isArray(对象) ? [] :Object.create(Object.getPrototypeOf(Object))/* --分配[[原型]]进行继承*/ seen.set(对象,对象)/* --使`_object`成为`object`的关联镜像*/ Reflect.ownKeys(object).forEach(key=>) defineProp(_object,key,{value:clone(object[key])},object) ) 返回对象(_O) } } 函数copyPropDescs(目标,源){ Object.defineProperties(目标, Object.getOwnPropertyDescriptors(源) ) } 功能转换器FnToStr(fn){ 让fnStr=字符串(fn) if(fn.name.startsWith(“[”))//是符号键 fnStr=fnStr.replace(/\[符号\..+?\]/,'') fnStr=/^(?!(异步)? (函数\b|[^{]+?=>)[^(]+?\(/.test(fnStr) ? fnStr.replace(/^(异步)? (\*)?/, “$1功能$2”):fnStr 返回fnStr } 函数copyFn(fn){ const newFn=新函数(`return${convertFnToStr(fn)}`)() copyPropDescs(新fn,fn) 返回newFn } 函数defineProp(object,key,descriptor={},copyFrom={}){ 常量{configurable:configurable,writable:writable} =Object.getOwnPropertyDescriptor(对象,键) ||{可配置:true,可写:true} const test=_configurable//可以重新定义属性 &&(_writable===未定义||_writble)//可以分配给属性 if(!test | | arguments.length<=2)返回测试 const basisDesc=对象.getOwnPropertyDescriptor(copyFrom,key) ||{configurable:true,writable:true}//自定义… || {}; // … 或保留为本机默认设置 [“get”,“set”,“value”,“writable”,“enumerable”,“configurable”] .forEach(属性=> 描述符[attr]===未定义&& (描述符[attr]=basisDesc[attr]) ) 常量{get,set,value,writable,enumerable,configurable} =描述符 return Object.defineProperty(对象,键{ 可枚举、可配置。。。 获取||集 ? {get,set}//访问器描述符 :{value,writible}//数据描述符 }) }
//测试
常量obj0={ u: 未定义, nul:空, t: 是的, 数字:9, 字符串:“”, sym:符号(“Symbol”), [符号(“e”)]:数学。 E、, arr:[[0],[1,2], d: 新日期(), 回复:/f/g, 获取g(){return 0}, o:{ n: 0, o: {f:函数(…参数){}} }, 传真:{ getAccessorStr(对象){ 返回[] .concat(。。。 Object.values(Object.getOwnPropertyDescriptors(对象)) .filter(desc=>desc.writable===未定义) .map(desc=>对象值(desc)) ) .filter(道具=>道具类型==“功能”) .map(字符串) }, f0:函数f0(){}, f1:函数(){}, a=>a/(a+1), f3:()=>0, f4(params){return param=>param+params}, f5:(a,b)=>({c=0}={})=>a+b+c } } defineProp(obj0,“s”,{set(v){this.s=v}}) defineProp(obj0.arr,“tint”,{value:{is:“non-enumerable”}}) obj0.arr[0].name=“嵌套数组” 设obj1=克隆(obj0) 目标1.o.n=1 obj1.o.o.g=函数g(a=0,b=0){return a+b} obj1.arr[1][1]=3 obj1.d.setTime(+obj0.d+60*1000) obj1.arr.tin.is=“可枚举?否” obj1.arr[0].name=“嵌套arr” defineProp(obj1,“s”,{set(v){this.s=v+1}}) defineProp(obj1.re,“多行”,{value:true}) 控制台.log(“\n\n”+“-”.repeat(2**6)) console.log(“>:>:测试-常规”) console.log(“obj0:\n”,JSON.stringify(obj0)) console.log(“obj1:\n”,JSON.stringify(obj1)) 控制台.log() 控制台.log(“obj0:\n”,obj0) 控制台.log(“obj1:\n”,obj1) 控制台.log() 控制台.log(“obj0\n”, “.arr.tint:”,obj0.arr.tint,“\n”, “.arr[0].name:”,对象0.arr[0].name ) 控制台.log(“obj1\n”, “.arr.tint:”,obj1.arr.tint,“\n”, “.arr[0].name:”,obj1.arr[0].name ) 控制台.log() console.log(“访问器类型描述符\n”, “obj0:”,obj0.f.getAccessorStr(obj0),“\n”, “obj1:”,obj1.f.getAccessorStr(obj1),“\n”, “集合(obj0&obj1).s:”,obj0.s=obj1.s=0,“\n”, “→(obj0,obj1)_ s: “,对象0.s,”,“,”,对象1.s ) console.log(“--obj0没有受到干扰。”) 控制台.log(“\n\n”+“-”.repeat(2**6)) log(“>:>:Test-更多类型的函数”) const fnsFor测试={ f(_){返回}, 函数:_=>_, aFunc:异步_=>_, 异步函数(){}, async-asyncFunc(){}, aFn:async函数(){}, *gen(){}, async*asyncGen(){}, aG1:异步函数*(){}, aG2:异步函数*gen(){}, *[Symbol.iterator](){yield*Object.keys(this)} } console.log(Reflect.ownKeys(fnsForTest).map(k=> `${字符串(k)}: ${fnsForTest[k].name}--> ${字符串(fnsForTest[k])}` ).join(“\n”) 常量normedFnsStr=`{ f: 函数f(_){return_}, 函数:_=>_, aFunc:异步_=>_, 函数:异步函数(){}, asyncFunc:异步函数asyncFunc(){}, aFn:异步函数(){}, gen:函数*gen(){}, asyncGen:异步函数*asyncGen(){}, aG1:异步函数*(){}, aG2:异步函数*gen(){}, [Symbol.iterator]:函数*(){yield*Object.keys(this)} }` const copiedFnsForTest=克隆(fnsForTest) console.log(“fnsForTest:”,fnsForTest) console.log(“fnsForTest(copyed):”,copyedFnsFortest) log(“fnsForTest(normedstr):”,eval(`(${normedFnsStr})`) console.log(“fnsForTest及其克隆的比较:”, Reflect.ownKeys(fnsForTest).map(k=> [k,fnsForTest[k]===复制fnsForTest[k]] ) ) 控制台.log(“\n\n”+“-”.repeat(2**6)) console.log(“>:>:测试-循环结构”) obj0.o.r={} obj0.o.r.递归=obj0.o obj0.arr[1]=对象0.arr obj1=克隆(obj0) console.log(“obj0:\n”,obj0) console.log(“obj1:\n”,obj1) console.log(“清除obj0的递归:”, obj0.o.r.recursive=null,obj0.arr[1]=1 ) 控制台.log( “obj0\n”, “.o.r:”,obj0.o.r,“\n”, “.arr:”,对象0.arr ) 控制台.log( “obj1\n”, “.o.r:”,obj1.o.r,“\n”, “.arr:”,obj1.arr ) console.log(“--obj1没有受到干扰。”) 控制台.log(“\n\n”+“-”.repeat(2**6)) console.log(“>:>:测试-类”) 类人员{ 建造师(姓名){ this.name=名称 } } 类Boy扩展Person{} Boy.prototype.sex=“M” const boy0=新男孩 boy0.abotion={运动:“太空飞行”} const boy1=克隆(boy0) boy1.hoby.sport=“超光速飞行” boy0.name=“一个” boy1.name=“neo” 控制台.log(“boy0:\n”,boy0) 控制台.log(“boy1:\n”,boy1) log(“boy1的原型===boy0的:”, Object.getPrototypeOf(boy1)===对象.getProtocypeOv(boy0) )
工具书类
使用的语言技巧
设obj1={ a: 0, b:{ c: 0, 电子邮箱:{ f: 0个 } } }; 设obj3=克隆(obj1); obj1.a=4; obj1.b.c=4; obj1.b.e.f=100; console.log(JSON.stringify(obj1)); //{“a”:4,“b”:{“c”:4 console.log(JSON.stringfy(obj3)); //{“a”:0,“b”:{“c”:4,“e”:{“f”:100}}}
设obj1={ a: 0, b:{ c: 0, 电子邮箱:{ f: 0个 } } }; 设obj3=_.cloneDeep(obj1); obj1.a=100; obj1.b.c=100; obj1.b.e.f=100; console.log(JSON.stringify(obj1)); {“a”:100,“b”:{“c”:100 console.log(JSON.stringify(obj3)); {“a”:0,“b”:{“c”:0