Heim  >  Artikel  >  Welche drei Implementierungsmethoden gibt es für Deep Copy?

Welche drei Implementierungsmethoden gibt es für Deep Copy?

coldplay.xixi
coldplay.xixiOriginal
2020-12-01 12:01:4535304Durchsuche

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;

Welche drei Implementierungsmethoden gibt es für Deep Copy?

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&A

Die 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

  • Zielobjekt vom Typ Objekt
  • , die Mitgliedseigenschaften anderer Objekte werden an dieses Objekt angehängt.

    Objekt1 ObjektN

  • optional. Objekttyp Das erste und N-te zusammengeführte Objekt.
  • let a=[0,1,[2,3],4],
            b=a.slice();
    a[0]=1;
    a[2][0]=1;
    console.log(a,b);
    Sie können sehen, dass der Effekt der gleiche ist wie bei der obigen Methode, Sie müssen sich jedoch auf die JQ-Bibliothek verlassen.

Nachdem ich so viel gesagt habe: Das Verständnis von Deep Copy dient nicht nur der Bewältigung von Interviewfragen, sondern ist auch bei der tatsächlichen Entwicklung sehr nützlich. Beispielsweise wird im Hintergrund eine Menge Daten zurückgegeben, und Sie müssen diese Datenmenge verarbeiten. Bei der Entwicklung mit mehreren Personen haben Sie jedoch keine Möglichkeit zu wissen, ob diese Datenmenge andere Funktionen hat Eine direkte Änderung kann zu versteckten Problemen führen. Das Kopieren kann Ihnen dabei helfen, Daten sicherer und sicherer zu verwenden. Dies ist wahrscheinlich das, was es bedeutet.

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);

Der Vorteil dieser Methode besteht darin, dass der Code relativ einfach zu schreiben ist. Aber auch die Mängel liegen auf der Hand. Sie erstellen zunächst einen temporären, möglicherweise großen String, um ihn dann wieder in den Parser einzufügen. Ein weiterer Nachteil besteht darin, dass diese Methode keine zyklischen Objekte verarbeiten kann. Das folgende Schleifenobjekt löst bei Verwendung dieser Methode eine Ausnahme aus

let a=[0,1,[2,3],4],
    b=$.extend(true,[],a);
a[0]=1;
a[2][0]=1;
console.log(a,b);

Zum Beispiel gehen Map, Set, RegExp, Date, ArrayBuffer und andere integrierte Typen während der Serialisierung verloren. Der Wert von

let obj = {a:{b:22}};let copy = JSON.parse(JSON.stringify(obj));
a wird wie folgt gedruckt

Welche drei Implementierungsmethoden gibt es für Deep Copy?

copy的值打印如下

Welche drei Implementierungsmethoden gibt es für Deep Copy?

对比发现,Set已丢失。

Structured Clone 结构化克隆算法

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()

支持循环对象,和大量的内置类型,对很多细节都处理的比较不错。推荐使用。

支持的类型有很多

Welche drei Implementierungsmethoden gibt es für Deep Copy?

我们这里再次关注一下lodash是如何解决循环应用这个问题的?

Welche drei Implementierungsmethoden gibt es für Deep Copy?

从相关的代码中。我们可以发现。lodash是用一个栈记录了。所有被拷贝的引用值。如果再次碰到同样的引用值的时候,不会再去拷贝一遍。而是利用之前已经拷贝好的值。

实现一个简易点的深拷贝,以解决循环引用的问题为目标

我们仅仅实现一个简易点的深拷贝。能优雅的处理循环引用的即可。在实现深拷贝之前,我们首先温习回顾一下js中的遍历对象的属性的方法和各种方法的优缺点。

js中遍历一个对象的属性的方法

  • Object.keys() 仅仅返回自身的可枚举属性,不包括继承来的,更不包括Symbol属性
  • Object.getOwnPropertyNames() 返回自身的可枚举和不可枚举属性。但是不包括Symbol属性
  • Object.getOwnPropertySymbols() 返回自身的Symol属性
  • for...in 可以遍历对象的自身的和继承的可枚举属性,不包含Symbol属性
  • Reflect.ownkeys() 返回对象自身的所有属性,不管是否可枚举,也不管是否是Symbol。注意不包括继承的属性

实现深拷贝,解决循环引用问题

/**
 * 判断是否是基本数据类型
 * @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!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Vorheriger Artikel:Welche Software ist au?Nächster Artikel:Welche Software ist au?