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

Lesezeit: 6 Minuten

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

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

Ich habe auch rekursive Kopierfunktionen mit verschiedenen Fehlern gesehen.

Ich bin überrascht, dass es keine kanonische Lösung gibt.

  • Eval ist nicht böse. Die Verwendung von eval ist schlecht. Wenn Sie Angst vor Nebenwirkungen haben, wenden Sie es falsch an. Die Nebenwirkungen, die Sie befürchten, sind die Gründe, es zu verwenden. Hat übrigens jemand deine Frage tatsächlich beantwortet?

    – James Andino

    22. März 2012 um 14:08 Uhr


  • Das Klonen von Objekten ist eine heikle Angelegenheit, insbesondere bei benutzerdefinierten Objekten beliebiger Sammlungen. Aus diesem Grund gibt es wahrscheinlich keine Out-of-the-Box-Methode.

    – b01

    11. März 2013 um 22:25 Uhr

  • eval() ist im Allgemeinen eine schlechte Idee, da die Optimierer vieler Javascript-Engines abschalten müssen, wenn sie mit Variablen arbeiten, die über gesetzt werden eval. Einfach haben eval() in Ihrem Code kann zu einer schlechteren Leistung führen.

    – user56reinstatemonica8

    8. September 2014 um 13:37 Uhr

  • Hier ist ein Leistungsvergleich zwischen den gängigsten Arten von Klonobjekten: jsben.ch/#/t917Z

    – EscapeNetscape

    17. Oktober 2016 um 9:58 Uhr

  • Beachten Sie, dass JSON -Methode verliert alle Javascript-Typen, die keine Entsprechung in JSON haben. Zum Beispiel: JSON.parse(JSON.stringify({a:null,b:NaN,c:Infinity,d:undefined,e:function(){},f:Number,g:false})) wird generieren {a: null, b: null, c: null, g: false}

    – Oriadam

    24. Mai 2017 um 13:06 Uhr


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

In meinen vorherigen Tests, bei denen Geschwindigkeit ein Hauptanliegen war, fand ich heraus

JSON.parse(JSON.stringify(obj))

um der langsamste Weg zu sein, ein Objekt tief zu klonen (es ist langsamer als jQuery.extend mit deep Flag um 10-20 % auf wahr gesetzt.

jQuery.extend ist ziemlich schnell, wenn die deep Flag ist gesetzt false (flacher Klon). Es ist eine gute Option, da es zusätzliche Logik für die Typvalidierung enthält und keine undefinierten Eigenschaften usw. kopiert, aber dies wird Sie auch ein wenig verlangsamen.

Wenn Sie die Struktur der Objekte kennen, die Sie klonen möchten, oder tief verschachtelte Arrays vermeiden können, können Sie eine einfache schreiben for (var i in obj) Schleife, um Ihr Objekt zu klonen, während hasOwnProperty überprüft wird, 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 einfach die Klonprozedur einbetten und das Objekt manuell erstellen.

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

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

Vorsicht bei der Verwendung von JSON.parse(JSON.stringify(obj)) Methode an Date Objekte – JSON.stringify(new Date()) gibt eine Zeichenfolgendarstellung des Datums im ISO-Format zurück, die JSON.parse() nicht zurückwandeln in a Date Objekt. Weitere Informationen finden Sie in dieser Antwort.

Beachten Sie außerdem, dass natives Klonen zumindest in Chrome 65 nicht der richtige Weg ist. Laut JSPerf ist das Durchführen des nativen Klonens durch Erstellen einer neuen Funktion fast 800x langsamer als die Verwendung von JSON.stringify, das auf ganzer Linie unglaublich schnell ist.

Update für ES6

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

Object.assign({}, obj);

  • Beachten Sie, dass es zwei Fehler in Ihrer Bank gibt: Erstens vergleicht sie ein wenig flaches Klonen (lodash _.clone und Object.assign) zu etwas tiefem Klonen (JSON.parse(JSON.stringify())). Zweitens heißt es “tiefer Klon” für Lodash, aber es macht stattdessen einen flachen Klon.

    – Papille

    15. Februar 2021 um 14:22 Uhr


  • Das ist einfach so falsch! Diese API sollte nicht auf diese Weise verwendet werden.

    – Fardin K.

    31. Juli 2014 um 23:34 Uhr


  • Als der Typ, der pushState in Firefox implementiert hat, empfinde ich eine seltsame Mischung aus Stolz und Abscheu vor diesem Hack. Gut gemacht Jungs.

    – Justin L.

    14. August 2014 um 18:37 Uhr

  • PushState- oder Notification-Hack funktioniert bei einigen Objekttypen wie Function nicht

    – Shishir Arora

    3. Juli 2019 um 20:06 Uhr

  • @ShishirArora Du hast recht, ich habe es gerade ausprobiert, es wirft eine ‘Uncaught DOMException: Das Objekt konnte nicht geklont werden.’ Dies gilt auch für den Notification-Hack.

    – ADJenks

    10. Juli 2020 um 21:39 Uhr


  • Weiß jemand zufällig, ob das native strukturierte Klonen von v8 anfällig für Prototypenverschmutzung ist?

    – Alexis Tyler

    12. November 2021 um 6:58 Uhr

Angenommen, Sie haben nur Eigenschaften und keine Funktionen in Ihrem Objekt, können Sie einfach Folgendes verwenden:

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

  • Objekte haben Eigenschaften, keine Variablen. 😉

    – RobG

    1. Oktober 2020 um 0:42 Uhr

  • Funktionen und Termine auch

    – vsync

    24. Oktober 2020 um 12:54 Uhr


  • Schlägt bei Objekten mit Circular-Eigenschaften fehl

    – Koushik Shom Choudhury

    7. September 2021 um 6:27 Uhr

  • oder Sets oder andere nicht-JSON-serialisierbare Eigenschaften

    – Andy Carlson

    8. Februar um 4:20 Uhr

Wenn es keine eingebaute gab, könnten Sie 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;
}

  • Objekte haben Eigenschaften, keine Variablen. 😉

    – RobG

    1. Oktober 2020 um 0:42 Uhr

  • Funktionen und Termine auch

    – vsync

    24. Oktober 2020 um 12:54 Uhr


  • Schlägt bei Objekten mit Circular-Eigenschaften fehl

    – Koushik Shom Choudhury

    7. September 2021 um 6:27 Uhr

  • oder Sets oder andere nicht-JSON-serialisierbare Eigenschaften

    – Andy Carlson

    8. Februar um 4:20 Uhr

Der effiziente Weg, ein Objekt in einer Codezeile zu klonen (nicht tief zu klonen).

Ein Object.assign -Methode ist Teil des ECMAScript 2015 (ES6)-Standards und macht genau das, was Sie brauchen.

var clone = Object.assign({}, obj);

Die Methode Object.assign() wird verwendet, um die Werte aller aufzählbaren eigenen Eigenschaften von einem oder mehreren Quellobjekten auf ein Zielobjekt zu kopieren.

Weiterlesen…

Die Polyfill um ältere Browser zu unterstützen:

if (!Object.assign) {
  Object.defineProperty(Object, 'assign', {
    enumerable: false,
    configurable: true,
    writable: true,
    value: function(target) {
      'use strict';
      if (target === undefined || target === null) {
        throw new TypeError('Cannot convert first argument to object');
      }

      var to = Object(target);
      for (var i = 1; i < arguments.length; i++) {
        var nextSource = arguments[i];
        if (nextSource === undefined || nextSource === null) {
          continue;
        }
        nextSource = Object(nextSource);

        var keysArray = Object.keys(nextSource);
        for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
          var nextKey = keysArray[nextIndex];
          var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
          if (desc !== undefined && desc.enumerable) {
            to[nextKey] = nextSource[nextKey];
          }
        }
      }
      return to;
    }
  });
}

  • Dies kopiert nicht rekursiv und bietet daher keine wirkliche Lösung für das Problem des Klonens eines Objekts.

    – mweiß

    8. März 2016 um 19:56 Uhr

  • Diese Methode hat funktioniert, obwohl ich einige getestet habe und _.extend({}, (obj)) mit Abstand am schnellsten war: 20x schneller als JSON.parse und 60% schneller als zum Beispiel Object.assign. Es kopiert alle Unterobjekte recht gut.

    – Nico

    9. Mai 2016 um 19:57 Uhr

  • @mwhite es gibt einen Unterschied zwischen Klon und Deep-Clone. Diese Antwort klont tatsächlich, aber sie klont nicht tief.

    – Meirion Hughes

    8. Juni 2016 um 12:08 Uhr

  • Die Frage betraf rekursive Kopien. Object.assign sowie die angegebene benutzerdefinierte Zuweisung werden nicht rekursiv kopiert

    – johannes_lalala

    4. März 2021 um 1:52 Uhr

986820cookie-checkWas ist der effizienteste Weg, um ein Objekt in JavaScript tief zu klonen?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy