Die drei Implementierungsmethoden von Deep Copy sind: 1. Rekursives Kopieren aller hierarchischen Attribute; 2. Implementieren mit Parse und Stringify von JSON-Objekten; 3. Ausleihen der Extend-Methode von JQ;
Die drei Implementierungsmethoden von Deep Copy sind:
1, Rekursive Rekursion zum Kopieren aller Ebenenattribute
function deepClone(obj){ let objClone = Array.isArray(obj)?[]:{}; if(obj && typeof obj==="object"){ for(key in obj){ if(obj.hasOwnProperty(key)){ //判断ojb子元素是否为对象,如果是,递归复制 if(obj[key]&&typeof obj[key] ==="object"){ objClone[key] = deepClone(obj[key]); }else{ //如果不是,简单复制 objClone[key] = obj[key]; } } } } return objClone; } let a=[1,2,3,4], b=deepClone(a); a[0]=2; console.log(a,b);
Wie zuvor vorgestellt, liegt b jetzt außerhalb der Kontrolle von a, nicht mehr von a betroffen.
Ich betone noch einmal, dass es beim Deep Copy darum geht, die Attribute jeder Ebene des Objekts zu kopieren. Sie können ein Beispiel sehen. Es gibt eine Extend-Methode in JQ, die auch Objekte kopieren kann. Durch tiefes Kopieren werden Attribute auf allen Ebenen weiterhin kopiert. Wir haben a in
geändert und die Kopie war zwar nicht vollständig, die Attribute der zweiten Ebene jedoch weiterhin konnten nicht erfolgreich kopiert werden und sind dennoch getrennt. Ohne die Kontrolle von a handelt es sich bei dem Slice überhaupt nicht um eine wirklich tiefe Kopie. Hier ist ein Zitat aus einem Bild in Zhihu Q&ADie Attribute der ersten Ebene werden tatsächlich tief kopiert und verfügen über ein unabhängiges Gedächtnis, aber die tieferen Attribute haben immer noch dieselbe Adresse, weshalb das oben genannte Problem verursacht wird . In ähnlicher Weise haben auch die Concat-Methode und Slice diese Situation. Sie müssen hier aufpassen. 2. Zusätzlich zur Rekursion können wir auch das Parsen und Stringifizieren von JSON-Objekten übernehmen
let a=[1,2,3,4], b=a.slice(); a[0]=2; console.log(a,b);
Sie können sehen, dass b jetzt von a völlig unberührt bleibt.
Übrigens können JSON.stringify und JSON.parse zusätzlich zur Implementierung von Deep Copy auch mit localStorage kombiniert werden, um einen Objekt-Array-Speicher zu implementieren. Bei Interesse können Sie diesen Artikel des Bloggers lesen.localStorage speichert Arrays, Objekte, localStorage, sessionStorage speichert Array-Objekte
3 Zusätzlich zu den beiden oben genannten Methoden können wir auch die Extend-Methode von JQ ausleihen.$.extend( [deep ], target, object1 [, objectN ] )
deep gibt an, ob tief kopiert werden soll. Wenn es wahr ist, handelt es sich um eine tiefe Kopie, wenn ja falsch, es handelt sich um eine flache Kopie
Ziel
Objekt1 ObjektN
let a=[0,1,[2,3],4],
b=a.slice();
a[0]=1;
a[2][0]=1;
console.log(a,b);
4.lodashs _.cloneDeep()
Das Folgende ist eine Lösung für das Problem der tiefen Kopie, die ich gelesen habe. JSON.parse
Konvertieren Sie zunächst ein Objekt in ein JSON-Objekt. Analysieren Sie dann dieses JSON-Objekt.
function deepClone(obj){ let _obj = JSON.stringify(obj), objClone = JSON.parse(_obj); return objClone } let a=[0,1,[2,3],4], b=deepClone(a); a[0]=1; a[2][0]=1; console.log(a,b);
let a=[0,1,[2,3],4], b=$.extend(true,[],a); a[0]=1; a[2][0]=1; console.log(a,b);
let obj = {a:{b:22}};let copy = JSON.parse(JSON.stringify(obj));
copy的值打印如下
对比发现,Set已丢失。
MessageChannel
建立两个端,一个端发送消息,另一个端接收消息。
function structuralClone(obj) { return new Promise(resolve =>{ const {port1, port2} = new MessageChannel(); port2.onmessage = ev => resolve(ev.data); port1.postMessage(obj); }) } const obj = /* ... */; structuralClone(obj).then(res=>{ console.log(res); })
这种方法的优点就是能解决循环引用的问题,还支持大量的内置数据类型。缺点就是这个方法是异步的。
History API
利用history.replaceState。这个api在做单页面应用的路由时可以做无刷新的改变url。这个对象使用结构化克隆,而且是同步的。但是我们需要注意,在单页面中不要把原有的路由逻辑搞乱了。所以我们在克隆完一个对象的时候,要恢复路由的原状。
function structuralClone(obj) { const oldState = history.state; history.replaceState(obj, document.title); const copy = history.state; history.replaceState(oldState, document.title); return copy; }var obj = {};var b = {obj}; obj.b = bvar copy = structuralClone(obj); console.log(copy);
这个方法的优点是。能解决循环对象的问题,也支持许多内置类型的克隆。并且是同步的。但是缺点就是有的浏览器对调用频率有限制。比如Safari 30 秒内只允许调用 100 次
Notification API
这个api主要是用于桌面通知的。如果你使用Facebook的时候,你肯定会发现时常在浏览器的右下角有一个弹窗,对就是这家伙。我们也可以利用这个api实现js对象的深拷贝。
function structuralClone(obj) { return new Notification('', {data: obj, silent: true}).data; }var obj = {};var b = {obj}; obj.b = bvar copy = structuralClone(obj); console.log(copy)
同样是优点和缺点并存,优点就是可以解决循环对象问题,也支持许多内置类型的克隆,并且是同步的。缺点就是这个需要api的使用需要向用户请求权限,但是用在这里克隆数据的时候,不经用户授权也可以使用。在http协议的情况下会提示你再https的场景下使用。
lodash的_.cloneDeep()
支持循环对象,和大量的内置类型,对很多细节都处理的比较不错。推荐使用。
支持的类型有很多
我们这里再次关注一下lodash是如何解决循环应用这个问题的?
从相关的代码中。我们可以发现。lodash是用一个栈记录了。所有被拷贝的引用值。如果再次碰到同样的引用值的时候,不会再去拷贝一遍。而是利用之前已经拷贝好的值。
我们仅仅实现一个简易点的深拷贝。能优雅的处理循环引用的即可。在实现深拷贝之前,我们首先温习回顾一下js中的遍历对象的属性的方法和各种方法的优缺点。
js中遍历一个对象的属性的方法
实现深拷贝,解决循环引用问题
/** * 判断是否是基本数据类型 * @param value */function isPrimitive(value){ return (typeof value === 'string' || typeof value === 'number' || typeof value === 'symbol' || typeof value === 'boolean') }/** * 判断是否是一个js对象 * @param value */function isObject(value){ return Object.prototype.toString.call(value) === "[object Object]"}/** * 深拷贝一个值 * @param value */function cloneDeep(value){ // 记录被拷贝的值,避免循环引用的出现 let memo = {}; function baseClone(value){ let res; // 如果是基本数据类型,则直接返回 if(isPrimitive(value)){ return value; // 如果是引用数据类型,我们浅拷贝一个新值来代替原来的值 }else if(Array.isArray(value)){ res = [...value]; }else if(isObject(value)){ res = {...value}; } // 检测我们浅拷贝的这个对象的属性值有没有是引用数据类型。如果是,则递归拷贝 Reflect.ownKeys(res).forEach(key=>{ if(typeof res[key] === "object" && res[key]!== null){ //此处我们用memo来记录已经被拷贝过的引用地址。以此来解决循环引用的问题 if(memo[res[key]]){ res[key] = memo[res[key]]; }else{ memo[res[key]] = res[key]; res[key] = baseClone(res[key]) } } }) return res; } return baseClone(value) }
验证我们写的cloneDeep是否能解决循环应用的问题
var obj = {};var b = {obj}; obj.b = bvar copy = cloneDeep(obj); console.log(copy);
Das obige ist der detaillierte Inhalt vonWelche drei Implementierungsmethoden gibt es für Deep Copy?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!