Es gibt eine Art Code-Wiederverwendungsmuster namens „Muster zum Kopieren von Eigenschaften“. Wenn wir an die Wiederverwendung von Code denken, denken wir höchstwahrscheinlich an die Vererbung von Code, aber es ist wichtig, sich an das ultimative Ziel zu erinnern: Wir wollen Code wiederverwenden. Vererbung ist nur ein Mittel zur Wiederverwendung von Code, nicht der einzige Weg. Das Kopieren von Eigenschaften ist ebenfalls ein Wiederverwendungsmuster, das sich von der Vererbung unterscheidet. In diesem Muster erhält ein Objekt Mitglieder von einem anderen Objekt, indem es diese einfach kopiert. Jeder, der jQuery verwendet hat, weiß, dass es nicht nur Plug-Ins von Drittanbietern erweitert, sondern auch zum Kopieren von Attributen verwendet werden kann. Werfen wir einen Blick auf den Implementierungscode einer Funktion „extend()“ (beachten Sie, dass dies nicht der Quellcode von jQuery ist, sondern nur ein einfaches Beispiel):
function extend(parent, child) { var i; //如果不传入第二参数child //那么就创建一个新的对象 child = child || {}; //遍历parent对象的所有属性 //并且过滤原型上的属性 //然后将自身属性复制到child对象上 for(i in parent) { if(parent.hasOwnProperty(i)) { child[i] = parent[i]; } } //返回目标对象child return child; }
Der obige Code ist eine einfache Implementierung, er durchläuft nur die Mitglieder des übergeordneten Objekts und kopiert sie in das untergeordnete Objekt. Testen wir es mit der oben genannten Methode „extend()“:
var dad = {name: "Adam"}; var kid = extend(dad); console.log(kid.name); //Adam
Wir haben festgestellt, dass die Methode „extend()“ bereits normal funktionieren kann. Es gibt jedoch ein Problem. Was oben angegeben ist, ist ein sogenannter flacher Klon. Wenn Sie bei der Verwendung einer flachen Kopie die Eigenschaften des untergeordneten Objekts ändern und die Eigenschaft zufällig ein Objekt ist, ändert dieser Vorgang auch das übergeordnete Objekt. In vielen Fällen ist dies nicht das gewünschte Ergebnis. Bedenken Sie Folgendes:
var dad = { counts: [1, 2, 3], reads: {paper: true} }; var kid = extend(dad) //调用extend()方法将dad的属性复制到kid上面 kid.counts.push(4); //把4追加到kid.counts数组里面 console.log(dad.counts); //[1, 2, 3, 4]
Anhand des obigen Beispiels werden wir feststellen, dass nach der Änderung des Attributs kid.counts (Hinzufügen von Element 4) auch dad.counts betroffen ist. Dies liegt daran, dass bei Verwendung einer flachen Kopie das Objekt als Referenz übergeben wird, d. h. kid.counts und dad.counts auf dasselbe Array verweisen (oder im Speicher auf dieselbe Heap-Adresse verweisen).
Als nächstes ändern wir die Funktion „extend()“, um tiefes Kopieren zu implementieren. Was wir tun müssen, ist, jedes Attribut des übergeordneten Objekts zu überprüfen und, wenn das Attribut zufällig ein Objekt ist, die Attribute des Objekts rekursiv zu kopieren. Darüber hinaus müssen Sie auch erkennen, ob es sich bei dem Objekt um ein Array handelt. Dies liegt daran, dass sich die Literalerstellungsmethode von Arrays von der Literalerstellungsmethode von Objekten unterscheidet. Ersteres ist [] und letzteres ist {}. Um ein Array zu erkennen, können Sie die Methode Object.prototype.toString() verwenden. Wenn es sich um ein Array handelt, wird „[object Array]“ zurückgegeben. Werfen wir einen Blick auf die Funktion „extend()“ der Deep-Copy-Version:
function extendDeep(parent, child) { child = child || {}; for(var i in parent) { if(parent.hasOwnProperty(i)) { //检测当前属性是否为对象 if(typeof parent[i] === "object") { //如果当前属性为对象,还要检测它是否为数组 //这是因为数组的字面量表示和对象的字面量表示不同 //前者是[],而后者是{} child[i] = (Object.prototype.toString.call(parent[i]) === "[object Array]") ? [] : {}; //递归调用extend extendDeep(parent[i], child[i]); } else { child[i] = parent[i]; } } } return child; }
Okay, die Deep-Copy-Funktion wurde geschrieben, um zu sehen, ob sie wie erwartet funktioniert, das heißt, ob Deep-Copy erreicht werden kann:
var dad = { counts: [1, 2, 3], reads: {paper: true} }; var kid = extendDeep(dad); //修改kid的counts属性和reads属性 kid.counts.push(4); kid.reads.paper = false; console.log(kid.counts); //[1, 2, 3, 4] console.log(kid.reads.paper); //false console.log(dad.counts); //[1, 2, 3] console.log(dad.reads.paper); //true
Anhand des obigen Beispiels können wir feststellen, dass sich die dad.counts und kid.reads des übergeordneten Objekts nicht geändert haben, selbst wenn die kid.counts und kid.reads des untergeordneten Objekts geändert wurden, sodass unser Ziel erreicht wurde.
Im Folgenden finden Sie eine Zusammenfassung der grundlegenden Ideen zur Implementierung von Deep Copy:
1. Überprüfen Sie, ob das aktuelle Attribut ein Objekt ist
2. Da es sich bei Arrays um spezielle Objekte handelt, muss festgestellt werden, ob es sich bei dem Attribut um ein Array handelt.
3. Wenn es sich um ein Array handelt, erstellen Sie ein leeres []-Array. Andernfalls erstellen Sie ein leeres {}-Objekt und weisen Sie es der aktuellen Eigenschaft des untergeordneten Objekts zu. Anschließend wird die Funktion „extendDeep“ rekursiv aufgerufen.
Das obige Beispiel verwendet eine Deep-Copy-Methode, die wir mithilfe eines rekursiven Algorithmus implementiert haben. Tatsächlich können die beiden vom neuen JSON-Objekt in ES5 bereitgestellten Methoden auch ein tiefes Kopieren erreichen, nämlich JSON.stringify() und JSON.parse(); ersteres wird zum Konvertieren des Objekts in eine Zeichenfolge verwendet, und letzteres wird verwendet um die Zeichenfolge in ein Objekt umzuwandeln. Im Folgenden verwenden wir diese Methode, um eine Deep-Copy-Funktion zu implementieren:
function extendDeep(parent, child) { var i, proxy; proxy = JSON.stringify(parent); //把parent对象转换成字符串 proxy = JSON.parse(proxy) //把字符串转换成对象,这是parent的一个副本 child = child || {}; for(i in proxy) { if(proxy.hasOwnProperty(i)) { child[i] = proxy[i]; } } proxy = null; //因为proxy是中间对象,可以将它回收掉 return child; }
Das Folgende ist ein Testbeispiel:
var dad = { counts: [1, 2, 3], reads: {paper: true} }; var kid = extendDeep(dad); //修改kid的counts属性和reads属性 kid.counts.push(4); kid.reads.paper = false; console.log(kid.counts); //[1, 2, 3, 4] console.log(kid.reads.paper); //false console.log(dad.counts); //[1, 2, 3] console.log(dad.reads.paper); //true
Bei Tests wurde festgestellt, dass auch tiefes Kopieren möglich ist. Im Allgemeinen wird die Verwendung der letzteren Methode empfohlen, da JSON.parse und JSON.stringify integrierte Funktionen sind und schneller verarbeitet werden. Darüber hinaus verwendet die vorherige Methode rekursive Aufrufe. Wir alle wissen, dass Rekursion ein relativ ineffizienter Algorithmus ist.
Hier geht es um die Implementierungsmethode von JavaScript Deep Clone. Ich hoffe, es wird Ihnen hilfreich sein!