网站首页 >> 游戏攻略 >> 正文
简介: 废话不多说,直入主题,直接上代码。??浅克隆之所以被称为浅克隆,是因为对象只会被克隆最外部的一层,至于更深层的对象,则依然是通过引用指向同一块堆内存.functionshallowClone(o){\

js深度克隆对象方法?对象的深浅克隆方法实现

废话不多说,直入主题,直接上代码。

??浅克隆之所以被称为浅克隆,是因为对象只会被克隆最外部的一层,至于更深层的对象,则依然是通过引用指向同一块堆内存.

functionshallowClone(o){\nconstobj={};\nfor(letiino){\nobj[i]=o[i];\n}\nreturnobj;\n}\n//被克隆对象\nconstoldObj={\na:1,\nb:['e','f','g'],\nc:{h:{i:2}}\n};\n\nconstnewObj=shallowClone(oldObj);\nconsole.log(newObj.c.h,oldObj.c.h);//{i:2}{i:2}\nconsole.log(oldObj.c.h===newObj.c.h);//true

我们可以看到,很明显虽然oldObj.c.h被克隆了,但是它还与oldObj.c.h相等,这表明他们依然指向同一段堆内存,这就造成了如果对newObj.c.h进行修改,也会影响oldObj.c.h,这就不是一版好的克隆。

newObj.c.h.i='change';\nconsole.log(newObj.c.h,oldObj.c.h);//{i:'change'}{i:'change'}

我们改变了newObj.c.h.i的值,oldObj.c.h.i也被改变了,这就是浅克隆的问题所在.

当然有一个新的apiObject.assign()也可以实现浅复制,但是效果跟上面没有差别,所以我们不再细说了.

2.1 *** ON.parse ***

前几年微博上流传着一个传说中最便捷实现深克隆的 *** ,

*** ON对象parse *** 可以将 *** ON字符串反序列化成 *** 对象,stringify *** 可以将 *** 对象序列化成 *** ON字符串,这两个 *** 结合起来就能产生一个便捷的深克隆.

constoldObj={\na:1,\nb:['e','f','g'],\nc:{h:{i:2}}\n};\n\nconstnewObj= *** ON.parse( *** ON.stringify(oldObj));\nconsole.log(newObj.c.h,oldObj.c.h);//{i:2}{i:2}\nconsole.log(oldObj.c.h===newObj.c.h);//false\nnewObj.c.h.i='change';\nconsole.log(newObj.c.h,oldObj.c.h);//{i:'change'}{i:2}

果然,这是一个实现深克隆的好 *** ,但是这个解决办法是不是太过简单了.

确实,这个 *** 虽然可以解决绝大部分是使用场景,但是却有很多坑:

//构造函数\nfunctionperson(pname){\nthis.name=pname;\n}\nconstMessi=newperson('Messi');\n//函数\nfunctionsay(){\nconsole.log('hi');\n};\nconstoldObj={\na:say,\nb:newArray(1),\nc:newRegExp('ab+c','i'),\nd:Messi\n};\nconstnewObj= *** ON.parse( *** ON.stringify(oldObj));\n//无法复制函数\nconsole.log(newObj.a,oldObj.a);//undefined[Function:say]\n//稀疏数组复制错误\nconsole.log(newObj.b[0],oldObj.b[0]);//nullundefined\n//无法复制正则对象\nconsole.log(newObj.c,oldObj.c);//{}/ab+c/i\n//构造函数指向错误\nconsole.log(newObj.d.constructor,oldObj.d.constructor);//[Function:Object][Function:person]

我们可以看到在对函数、正则对象、稀疏数组等对象克隆时会发生意外,构造函数指向也会发生错误。

constoldObj={};对象的循环引用会抛出错误.\n\noldObj.a=oldObj;\n\nconstnewObj= *** ON.parse( *** ON.stringify(oldObj));\nconsole.log(newObj.a,oldObj.a);//TypeError:Convertingcircularstructureto *** ON

对象的循环引用会抛出错误.。

2.2实现一个深拷贝函数

如果要避免上面的问题,那我们需要对不同的对象(正则、数组、Date等)要采用不同的处理方式,我们需要实现一个对象类型判断函数。

constisType=(obj,type)=>{\nif(typeofobj!=='object')returnfalse;\nconsttypeString=Object.prototype.toString.call(obj);\nletflag;\nswitch(type){\ncase'Array':\nflag=typeString==='[objectArray]';\nbreak;\ncase'Date':\nflag=typeString==='[objectDate]';\nbreak;\ncase'RegExp':\nflag=typeString==='[objectRegExp]';\nbreak;\ndefault:\nflag=false;\n}\nreturnflag;\n};

测试代码:

constarr=Array.of(3,4,5,2);\n\nconsole.log(isType(arr,'Array'));//true

做好了这些准备工作,我们就可以进行深克隆的实现了。下面直接上代码:

constclone=parent=>{\n//维护两个储存循环引用的数组\nconstparents=[];\nconstchildren=[];\n\nconst_clone=parent=>{\nif(parent===null)returnnull;\nif(typeofparent!=='object')returnparent;\n\nletchild,proto;\n\nif(isType(parent,'Array')){\n//对数组做特殊处理\nchild=[];\n}elseif(isType(parent,'RegExp')){\n//对正则对象做特殊处理\nchild=newRegExp(parent.source,getRegExp(parent));\nif(parent.lastIndex)child.lastIndex=parent.lastIndex;\n}elseif(isType(parent,'Date')){\n//对Date对象做特殊处理\nchild=newDate(parent.getTime());\n}else{\n//处理对象原型\nproto=Object.getPrototypeOf(parent);\n//利用Object.create切断原型链\nchild=Object.create(proto);\n}\n\n//处理循环引用\nconstindex=parents.indexOf(parent);\n\nif(index!=-1){\n//如果父数组存在本对象,说明之前已经被引用过,直接返回此对象\nreturnchildren[index];\n}\nparents.push(parent);\nchildren.push(child);\n\nfor(letiinparent){\n//递归\nchild[i]=_clone(parent[i]);\n}\n\nreturnchild;\n};\nreturn_clone(parent);\n};

我们做个测试:

functionperson(pname){\nthis.name=pname;\n}\n\nconstMessi=newperson('Messi');\n\nfunctionsay(){\nconsole.log('hi');\n}\n\nconstoldObj={\na:say,\nc:newRegExp('ab+c','i'),\nd:Messi,\n};\n\noldObj.b=oldObj;\n\n\nconstnewObj=clone(oldObj);\nconsole.log(newObj.a,oldObj.a);//[Function:say][Function:say]\nconsole.log(newObj.b,oldObj.b);//{a:[Function:say],c:/ab+c/i,d:person{name:'Messi'},b:[Circular]}{a:[Function:say],c:/ab+c/i,d:person{name:'Messi'},b:[Circular]}\nconsole.log(newObj.c,oldObj.c);///ab+c/i/ab+c/i\nconsole.log(newObj.d.constructor,oldObj.d.constructor);//[Function:person][Function:person]总结

这个深克隆函数能满足大部分的日常需求,还不算完美,例如Buffer对象、Promise、Set、Map可能都需要我们做特殊处理,另外对于确保没有循环引用的对象,我们可以省去对循环引用的特殊处理,因为这很消耗时间,不过一个基本的深克隆函数我们已经实现了。

如果在生产环境中更好用npm上的lodash的深克隆实现。

网友点评

博博常识网

博博常识网

www.kissing2lips.com

日常生活里,经常会碰到一些五花八门的小困难。不过好久好在有困难就有方法,如果你足够的细心,你会发现这些小困难都有着对应的小方法。

Powered By Z-BlogPHP Theme By . 鲁ICP备2021032584号-5