5167 Stimmen

Was ist der effizienteste Weg, ein Objekt in JavaScript zu klonen?

Was ist der effizienteste Weg, um ein JavaScript-Objekt zu klonen? Ich habe gesehen obj = eval(uneval(o)); verwendet werden, sondern das nicht dem Standard entspricht und nur von Firefox unterstützt wird .

Ich habe Dinge getan wie obj = JSON.parse(JSON.stringify(o)); stellen aber die Effizienz in Frage.

Ich habe auch rekursive Kopierfunktionen mit verschiedenen Fehlern gesehen.
Ich bin überrascht, dass es keine kanonische Lösung gibt.

566 Stimmen

Eval ist nicht böse. Schlechtes Benutzen von Eval schon. Wenn Sie Angst vor seinen Nebenwirkungen haben, verwenden Sie es falsch. Die Nebenwirkungen, die Sie fürchten, sind der Grund, warum Sie es verwenden. Hat eigentlich irgendjemand Ihre Frage beantwortet?

15 Stimmen

Das Klonen von Objekten ist eine knifflige Angelegenheit, insbesondere bei benutzerdefinierten Objekten beliebiger Sammlungen. Wahrscheinlich gibt es deshalb keine fertige Methode, dies zu tun.

12 Stimmen

eval() ist im Allgemeinen eine schlechte Idee, weil viele Javascript-Engine-Optimierer abschalten müssen, wenn sie mit Variablen arbeiten, die über eval . Nur mit eval() in Ihrem Code kann zu einer schlechteren Leistung führen.

5838voto

John Resig Punkte 34001

Natives tiefes Klonen

Es gibt jetzt einen JS-Standard namens "Strukturiertes Klonen" die experimentell in Node 11 und höher funktioniert, in Browsern landen wird, und die über Polyfills für bestehende Systeme .

structuredClone(value)

Falls erforderlich, laden Sie zuerst das Polyfill:

import structuredClone from '@ungap/structured-clone';

Ver diese Antwort für weitere Einzelheiten.

Ältere Antworten

Schnelles Klonen mit Datenverlust - JSON.parse/stringify

Wenn Sie nicht Date s, Funktionen, undefined , Infinity RegExps, Maps, Sets, Blobs, FileLists, ImageDatas, sparse Arrays, Typed Arrays oder andere komplexe Typen innerhalb Ihres Objekts, ist ein sehr einfacher One-Liner, um ein Objekt tief zu klonen:

JSON.parse(JSON.stringify(object))

const a = {
  string: 'string',
  number: 123,
  bool: false,
  nul: null,
  date: new Date(),  // stringified
  undef: undefined,  // lost
  inf: Infinity,  // forced to 'null'
  re: /.*/,  // lost
}
console.log(a);
console.log(typeof a.date);  // Date object
const clone = JSON.parse(JSON.stringify(a));
console.log(clone);
console.log(typeof clone.date);  // result of .toISOString()

Ver Corbans Antwort für Benchmarks.

Zuverlässiges Klonen unter Verwendung einer Bibliothek

Da das Klonen von Objekten nicht trivial ist (komplexe Typen, zirkuläre Referenzen, Funktionen usw.), bieten die meisten großen Bibliotheken Funktionen zum Klonen von Objekten. Erfinden Sie das Rad nicht neu - wenn Sie bereits eine Bibliothek verwenden, prüfen Sie, ob diese eine Funktion zum Klonen von Objekten hat. Zum Beispiel,

  • lodash - cloneDeep ; können separat über die Funktion lodash.clonedeep Modul und ist wahrscheinlich die beste Wahl, wenn Sie nicht bereits eine Bibliothek verwenden, die eine tiefe Klonfunktion bietet
  • AngularJS - angular.copy
  • jQuery - jQuery.extend(true, { }, oldObject) ; .clone() klont nur DOM-Elemente
  • nur Bibliothek - just-clone Teil einer Bibliothek von abhängigkeitsfreien npm-Modulen, die nur eine Sache tun. Schuldfreie Dienstprogramme für jede Gelegenheit.

ES6 ( flach Kopie)

Der Vollständigkeit halber sei angemerkt, dass ES6 zwei flache Kopiermechanismen bietet: Object.assign() y el Verbreitungssyntax . die Werte aller aufzählbaren eigenen Eigenschaften von einem Objekt zum anderen kopiert. Zum Beispiel:

var A1 = {a: "2"};
var A2 = Object.assign({}, A1);
var A3 = {...A1};  // Spread Syntax

2484voto

Corban Brook Punkte 21070

Sehen Sie sich diesen Benchmark an: http://jsben.ch/#/bWfk9

Bei meinen früheren Tests, bei denen die Geschwindigkeit im Vordergrund stand, stellte ich fest

JSON.parse(JSON.stringify(obj))

ist der langsamste Weg, ein Objekt tief zu klonen (langsamer als jQuery.extend con deep Flagge um 10-20% auf "true" gesetzt).

jQuery.extend ist ziemlich schnell, wenn die deep Flagge ist gesetzt auf false (oberflächlicher Klon). Dies ist eine gute Option, da sie einige zusätzliche Logik für die Typvalidierung enthält und keine undefinierten Eigenschaften usw. kopiert, aber sie wird auch etwas langsamer.

Wenn Sie die Struktur der Objekte, die Sie klonen wollen, kennen oder tief verschachtelte Arrays vermeiden können, können Sie eine einfache for (var i in obj) Schleife, um Ihr Objekt zu klonen, während die Überprüfung hasOwnProperty und es wird viel viel schneller als jQuery sein.

Wenn Sie schließlich versuchen, eine bekannte Objektstruktur in einer heißen Schleife zu klonen, können Sie VIEL MEHR LEISTUNG erzielen, indem Sie die Klon-Prozedur einfach einbetten und das Objekt manuell konstruieren.

JavaScript-Trace-Maschinen sind schlecht bei der Optimierung for..in Schleifen und die Überprüfung von hasOwnProperty verlangsamen Sie ebenfalls. Manuelles Klonen, wenn Geschwindigkeit ein absolutes Muss ist.

var clonedObject = {
  knownProp: obj.knownProp,
  ..
}

Vorsicht bei der Verwendung der JSON.parse(JSON.stringify(obj)) Methode auf Date Objekte - JSON.stringify(new Date()) gibt eine Zeichenkette zurück, die das Datum im ISO-Format darstellt, das JSON.parse() n'a pas zurückverwandeln in eine Date Objekt. Siehe diese Antwort für weitere Details .

Bitte beachten Sie außerdem, dass zumindest in Chrome 65 das native Klonen nicht der richtige Weg ist. Laut JSPerf ist die Durchführung des nativen Klonens durch Erstellen einer neuen Funktion fast 800x langsamer als die Verwendung von JSON.stringify, die durchweg unglaublich schnell ist.

Update für ES6

Wenn Sie Javascript ES6 verwenden, probieren Sie diese native Methode zum Klonen oder flachen Kopieren aus.

Object.assign({}, obj);

658voto

Jeremy Punkte 1

Strukturiertes Klonen

2022 Aktualisierung: Die structuredClone globale Funktion ist bereits in Firefox 94, Node 17 und Deno 1.14 verfügbar

Der HTML-Standard umfasst einen internen strukturierten Algorithmus zum Klonen/Serialisieren die tiefe Klone von Objekten erstellen kann. Es ist immer noch auf bestimmte eingebaute Typen beschränkt, aber zusätzlich zu den wenigen von JSON unterstützten Typen unterstützt es auch Dates, RegExps, Maps, Sets, Blobs, FileLists, ImageDatas, sparse Arrays, Typed Arrays und in Zukunft wahrscheinlich noch mehr. Außerdem werden Referenzen innerhalb der geklonten Daten beibehalten, so dass zyklische und rekursive Strukturen unterstützt werden, die bei JSON zu Fehlern führen würden.

Unterstützung in Node.js:

El structuredClone globale Funktion wird von Node 17.0 bereitgestellt:

const clone = structuredClone(original);

Frühere Versionen: Die v8 Modul in Node.js (ab Node 11) stellt die strukturierte Serialisierungs-API direkt zur Verfügung Diese Funktion ist jedoch noch als "experimentell" gekennzeichnet und kann in zukünftigen Versionen geändert oder entfernt werden. Wenn Sie eine kompatible Version verwenden, ist das Klonen eines Objekts so einfach wie:

const v8 = require('v8');

const structuredClone = obj => {
  return v8.deserialize(v8.serialize(obj));
};

Direkte Unterstützung in Browsern: Verfügbar in Firefox 94

El structuredClone globale Funktion wird bald von allen wichtigen Browsern angeboten werden (dies wurde bereits in whatwg/html#793 auf GitHub ). Es sieht / wird so aussehen:

const clone = structuredClone(original);

Solange dies nicht der Fall ist, sind die strukturierten Klonimplementierungen der Browser nur indirekt sichtbar.

Asynchrone Abhilfe: Verwendbar.

Die kostengünstigere Möglichkeit, einen strukturierten Klon mit bestehenden APIs zu erstellen, besteht darin, die Daten über einen Port einer MessageChannels . Der andere Anschluss sendet ein message Ereignis mit einem strukturierten Klon des angehängten .data . Leider ist das Abhören dieser Ereignisse notwendigerweise asynchron, und die synchronen Alternativen sind weniger praktisch.

class StructuredCloner {
  constructor() {
    this.pendingClones_ = new Map();
    this.nextKey_ = 0;

    const channel = new MessageChannel();
    this.inPort_ = channel.port1;
    this.outPort_ = channel.port2;

    this.outPort_.onmessage = ({data: {key, value}}) => {
      const resolve = this.pendingClones_.get(key);
      resolve(value);
      this.pendingClones_.delete(key);
    };
    this.outPort_.start();
  }

  cloneAsync(value) {
    return new Promise(resolve => {
      const key = this.nextKey_++;
      this.pendingClones_.set(key, resolve);
      this.inPort_.postMessage({key, value});
    });
  }
}

const structuredCloneAsync = window.structuredCloneAsync =
    StructuredCloner.prototype.cloneAsync.bind(new StructuredCloner);

Beispiel Verwendung:

const main = async () => {
  const original = { date: new Date(), number: Math.random() };
  original.self = original;

  const clone = await structuredCloneAsync(original);

  // They're different objects:
  console.assert(original !== clone);
  console.assert(original.date !== clone.date);

  // They're cyclical:
  console.assert(original.self === original);
  console.assert(clone.self === clone);

  // They contain equivalent values:
  console.assert(original.number === clone.number);
  console.assert(Number(original.date) === Number(clone.date));

  console.log("Assertions complete.");
};

main();

Synchrone Umgehungen: Schrecklich!

Es gibt keine guten Optionen für die synchrone Erstellung strukturierter Klone. Hier sind stattdessen ein paar unpraktische Hacks.

history.pushState() y history.replaceState() erstellen beide einen strukturierten Klon ihres ersten Arguments und weisen diesen Wert dem history.state . Auf diese Weise können Sie einen strukturierten Klon eines beliebigen Objekts wie dieses erstellen:

const structuredClone = obj => {
  const oldState = history.state;
  history.replaceState(obj, null);
  const clonedObj = history.state;
  history.replaceState(oldState, null);
  return clonedObj;
};

Beispiel Verwendung:

'use strict';

const main = () => {
  const original = { date: new Date(), number: Math.random() };
  original.self = original;

  const clone = structuredClone(original);

  // They're different objects:
  console.assert(original !== clone);
  console.assert(original.date !== clone.date);

  // They're cyclical:
  console.assert(original.self === original);
  console.assert(clone.self === clone);

  // They contain equivalent values:
  console.assert(original.number === clone.number);
  console.assert(Number(original.date) === Number(clone.date));

  console.log("Assertions complete.");
};

const structuredClone = obj => {
  const oldState = history.state;
  history.replaceState(obj, null);
  const clonedObj = history.state;
  history.replaceState(oldState, null);
  return clonedObj;
};

main();

Dies ist zwar synchron, kann aber extrem langsam sein. Es fällt der gesamte Overhead an, der mit der Bearbeitung des Browserverlaufs verbunden ist. Ein wiederholter Aufruf dieser Methode kann dazu führen, dass Chrome vorübergehend nicht mehr reagiert.

El Notification Konstrukteur erstellt einen strukturierten Klon der zugehörigen Daten. Es wird auch versucht, dem Benutzer eine Browser-Benachrichtigung anzuzeigen, was jedoch fehlschlägt, es sei denn, Sie haben eine Benachrichtigungserlaubnis beantragt. Falls Sie die Erlaubnis für andere Zwecke haben, wird die von uns erstellte Benachrichtigung sofort geschlossen.

const structuredClone = obj => {
  const n = new Notification('', {data: obj, silent: true});
  n.onshow = n.close.bind(n);
  return n.data;
};

Beispiel Verwendung:

'use strict';

const main = () => {
  const original = { date: new Date(), number: Math.random() };
  original.self = original;

  const clone = structuredClone(original);

  // They're different objects:
  console.assert(original !== clone);
  console.assert(original.date !== clone.date);

  // They're cyclical:
  console.assert(original.self === original);
  console.assert(clone.self === clone);

  // They contain equivalent values:
  console.assert(original.number === clone.number);
  console.assert(Number(original.date) === Number(clone.date));

  console.log("Assertions complete.");
};

const structuredClone = obj => {
  const n = new Notification('', {data: obj, silent: true});
  n.close();
  return n.data;
};

main();

566voto

Sultan Shakir Punkte 704

Unter der Annahme, dass Sie nur Eigenschaften und keine Funktionen in Ihrem Objekt haben, können Sie einfach verwenden:

var newObject = JSON.parse(JSON.stringify(oldObject));

380voto

ConroyP Punkte 39832

Wenn es kein eingebautes System gibt, können Sie es versuchen:

function clone(obj) {
    if (obj === null || typeof (obj) !== 'object' || 'isActiveClone' in obj)
        return obj;

    if (obj instanceof Date)
        var temp = new obj.constructor(); //or new Date(obj);
    else
        var temp = obj.constructor();

    for (var key in obj) {
        if (Object.prototype.hasOwnProperty.call(obj, key)) {
            obj['isActiveClone'] = null;
            temp[key] = clone(obj[key]);
            delete obj['isActiveClone'];
        }
    }
    return temp;
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X