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




js shallow object copy (24)

Flacher Exemplar-Einzeiler ( ECMAScript 5. Auflage ):

var origin = { foo : {} };
var copy = Object.keys(origin).reduce(function(c,k){c[k]=origin[k];return c;},{});

console.log(origin, copy);
console.log(origin == copy); // false
console.log(origin.foo == copy.foo); // true

Ein flacher Exemplar ( ECMAScript 6. Ausgabe , 2015):

var origin = { foo : {} };
var copy = Object.assign({}, origin);

console.log(origin, copy);
console.log(origin == copy); // false
console.log(origin.foo == copy.foo); // true

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

Ich habe Dinge wie obj = JSON.parse(JSON.stringify(o)); aber frag die Effizienz.

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


Nur weil ich AngularJS erwähnt habe und gedacht habe, dass die Leute vielleicht wissen wollen ...

angular.copy bietet auch eine Methode zum tiefen Kopieren von Objekten und Arrays.


Code:

// extends 'from' object with members from 'to'. If 'to' is null, a deep clone of 'from' is returned
function extend(from, to)
{
    if (from == null || typeof from != "object") return from;
    if (from.constructor != Object && from.constructor != Array) return from;
    if (from.constructor == Date || from.constructor == RegExp || from.constructor == Function ||
        from.constructor == String || from.constructor == Number || from.constructor == Boolean)
        return new from.constructor(from);

    to = to || new from.constructor();

    for (var name in from)
    {
        to[name] = typeof to[name] == "undefined" ? extend(from[name], null) : to[name];
    }

    return to;
}

Prüfung:

var obj =
{
    date: new Date(),
    func: function(q) { return 1 + q; },
    num: 123,
    text: "asdasd",
    array: [1, "asd"],
    regex: new RegExp(/aaa/i),
    subobj:
    {
        num: 234,
        text: "asdsaD"
    }
}

var clone = extend(obj);

Cloning eines Objekts war in JS immer ein Problem, aber vor ES6 ging es darum, verschiedene Möglichkeiten zum Kopieren eines Objekts in JavaScript unten aufzuführen. Stellen Sie sich vor, Sie haben das Objekt unten und möchten eine tiefe Kopie davon haben:

var obj = {a:1, b:2, c:3, d:4};

Es gibt einige Möglichkeiten, dieses Objekt zu kopieren, ohne den Ursprung zu ändern:

1) ES5 +, Verwenden einer einfachen Funktion zum Kopieren für Sie:

function deepCopyObj(obj) {
    if (null == obj || "object" != typeof obj) return obj;
    if (obj instanceof Date) {
        var copy = new Date();
        copy.setTime(obj.getTime());
        return copy;
    }
    if (obj instanceof Array) {
        var copy = [];
        for (var i = 0, len = obj.length; i < len; i++) {
            copy[i] = cloneSO(obj[i]);
        }
        return copy;
    }
    if (obj instanceof Object) {
        var copy = {};
        for (var attr in obj) {
            if (obj.hasOwnProperty(attr)) copy[attr] = cloneSO(obj[attr]);
        }
        return copy;
    }
    throw new Error("Unable to copy obj this object.");
}

2) ES5 + mit JSON.parse und JSON.stringify.

var  deepCopyObj = JSON.parse(JSON.stringify(obj));

3) AngularJs:

var  deepCopyObj = angular.copy(obj);

4) jQuery:

var deepCopyObj = jQuery.extend(true, {}, obj);

5) UnderscoreJs & Loadash:

var deepCopyObj = _.cloneDeep(obj); //latest version UndescoreJs makes shallow copy

Hoffe diese Hilfe ...


Hier ist eine Version von ConroyPs Antwort, die auch dann funktioniert, wenn der Konstruktor erforderliche Parameter hat:

//If Object.create isn't already defined, we just do the simple shim,
//without the second argument, since that's all we need here
var object_create = Object.create;
if (typeof object_create !== 'function') {
    object_create = function(o) {
        function F() {}
        F.prototype = o;
        return new F();
    };
}

function deepCopy(obj) {
    if(obj == null || typeof(obj) !== 'object'){
        return obj;
    }
    //make sure the returned object has the same prototype as the original
    var ret = object_create(obj.constructor.prototype);
    for(var key in obj){
        ret[key] = deepCopy(obj[key]);
    }
    return ret;
}

Diese Funktion ist auch in meiner simpleoo Bibliothek verfügbar .

Bearbeiten:

Hier ist eine robustere Version (dank Justin McCandless werden jetzt auch zyklische Referenzen unterstützt):

/**
 * Deep copy an object (make copies of all its object properties, sub-properties, etc.)
 * An improved version of http://keithdevens.com/weblog/archive/2007/Jun/07/javascript.clone
 * that doesn't break if the constructor has required parameters
 * 
 * It also borrows some code from http://.com/a/11621004/560114
 */ 
function deepCopy(src, /* INTERNAL */ _visited, _copiesVisited) {
    if(src === null || typeof(src) !== 'object'){
        return src;
    }

    //Honor native/custom clone methods
    if(typeof src.clone == 'function'){
        return src.clone(true);
    }

    //Special cases:
    //Date
    if(src instanceof Date){
        return new Date(src.getTime());
    }
    //RegExp
    if(src instanceof RegExp){
        return new RegExp(src);
    }
    //DOM Element
    if(src.nodeType && typeof src.cloneNode == 'function'){
        return src.cloneNode(true);
    }

    // Initialize the visited objects arrays if needed.
    // This is used to detect cyclic references.
    if (_visited === undefined){
        _visited = [];
        _copiesVisited = [];
    }

    // Check if this object has already been visited
    var i, len = _visited.length;
    for (i = 0; i < len; i++) {
        // If so, get the copy we already made
        if (src === _visited[i]) {
            return _copiesVisited[i];
        }
    }

    //Array
    if (Object.prototype.toString.call(src) == '[object Array]') {
        //[].slice() by itself would soft clone
        var ret = src.slice();

        //add it to the visited array
        _visited.push(src);
        _copiesVisited.push(ret);

        var i = ret.length;
        while (i--) {
            ret[i] = deepCopy(ret[i], _visited, _copiesVisited);
        }
        return ret;
    }

    //If we've reached here, we have a regular object

    //make sure the returned object has the same prototype as the original
    var proto = (Object.getPrototypeOf ? Object.getPrototypeOf(src): src.__proto__);
    if (!proto) {
        proto = src.constructor.prototype; //this line would probably only be reached by very old browsers 
    }
    var dest = object_create(proto);

    //add this object to the visited array
    _visited.push(src);
    _copiesVisited.push(dest);

    for (var key in src) {
        //Note: this does NOT preserve ES5 property attributes like 'writable', 'enumerable', etc.
        //For an example of how this could be modified to do so, see the singleMixin() function
        dest[key] = deepCopy(src[key], _visited, _copiesVisited);
    }
    return dest;
}

//If Object.create isn't already defined, we just do the simple shim,
//without the second argument, since that's all we need here
var object_create = Object.create;
if (typeof object_create !== 'function') {
    object_create = function(o) {
        function F() {}
        F.prototype = o;
        return new F();
    };
}

Es gibt eine Bibliothek (genannt "Klon") , die das ganz gut macht. Es bietet das vollständigste rekursive Klonen / Kopieren beliebiger Objekte, die mir bekannt sind. Es unterstützt auch Zirkelverweise, die von den anderen Antworten noch nicht behandelt werden.

Sie finden es auch auf npm . Es kann sowohl für den Browser als auch für Node.js verwendet werden.

Hier ist ein Beispiel, wie man es benutzt:

Installiere es mit

npm install clone

oder mit Ender verpacken.

ender build clone [...]

Sie können den Quellcode auch manuell herunterladen.

Dann können Sie es in Ihrem Quellcode verwenden.

var clone = require('clone');

var a = { foo: { bar: 'baz' } };  // inital value of a
var b = clone(a);                 // clone a -> b
a.foo.bar = 'foo';                // change a

console.log(a);                   // { foo: { bar: 'foo' } }
console.log(b);                   // { foo: { bar: 'baz' } }

(Haftungsausschluss: Ich bin der Autor der Bibliothek.)


Lodash hat eine nette lodash.com/docs#cloneDeep -Methode:

var objects = [{ 'a': 1 }, { 'b': 2 }];

var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false

Das verwende ich:

function cloneObject(obj) {
    var clone = {};
    for(var i in obj) {
        if(typeof(obj[i])=="object" && obj[i] != null)
            clone[i] = cloneObject(obj[i]);
        else
            clone[i] = obj[i];
    }
    return clone;
}

Tiefes Kopieren von Objekten in JavaScript (ich denke, das Beste und das Einfachste)

1. JSON.parse verwenden (JSON.stringify (object));

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}
var newObj = JSON.parse(JSON.stringify(obj));
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } } 

2.Verwendung der erstellten Methode

function cloneObject(obj) {
    var clone = {};
    for(var i in obj) {
        if(obj[i] != null &&  typeof(obj[i])=="object")
            clone[i] = cloneObject(obj[i]);
        else
            clone[i] = obj[i];
    }
    return clone;
}

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}
var newObj = cloneObject(obj);
obj.b.c = 20;

console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } } 

3. Verwenden Sie den _.cloneDeep- Link lodashLo-Dashlodash

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}

var newObj = _.cloneDeep(obj);
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } } 

4. Verwenden der Object.assign () -Methode

var obj = { 
  a: 1,
  b: 2
}

var newObj = _.clone(obj);
obj.b = 20;
console.log(obj); // { a: 1, b: 20 }
console.log(newObj); // { a: 1, b: 2 }  

ABER FALSCH, WANN

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}

var newObj = Object.assign({}, obj);
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 20 } } --> WRONG
// Note: Properties on the prototype chain and non-enumerable properties cannot be copied.

5.Verwenden von Underscore.js _.clone- Link Underscore.js

var obj = { 
  a: 1,
  b: 2
}

var newObj = _.clone(obj);
obj.b = 20;
console.log(obj); // { a: 1, b: 20 }
console.log(newObj); // { a: 1, b: 2 }  

ABER FALSCH, WANN

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}

var newObj = _.cloneDeep(obj);
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 20 } } --> WRONG
// (Create a shallow-copied clone of the provided plain object. Any nested objects or arrays will be copied by reference, not duplicated.)

Referenzmedium.com

JSBEN.CH Performance Benchmarking Playground 1 ~ 3 http://jsben.ch/KVQLd


var clone = function() {
    var newObj = (this instanceof Array) ? [] : {};
    for (var i in this) {
        if (this[i] && typeof this[i] == "object") {
            newObj[i] = this[i].clone();
        }
        else
        {
            newObj[i] = this[i];
        }
    }
    return newObj;
}; 

Object.defineProperty( Object.prototype, "clone", {value: clone, enumerable: false});

Dies ist im Allgemeinen nicht die effizienteste Lösung, aber es macht, was ich brauche. Einfache Testfälle unten ...

function clone(obj, clones) {
    // Makes a deep copy of 'obj'. Handles cyclic structures by
    // tracking cloned obj's in the 'clones' parameter. Functions 
    // are included, but not cloned. Functions members are cloned.
    var new_obj,
        already_cloned,
        t = typeof obj,
        i = 0,
        l,
        pair; 

    clones = clones || [];

    if (obj === null) {
        return obj;
    }

    if (t === "object" || t === "function") {

        // check to see if we've already cloned obj
        for (i = 0, l = clones.length; i < l; i++) {
            pair = clones[i];
            if (pair[0] === obj) {
                already_cloned = pair[1];
                break;
            }
        }

        if (already_cloned) {
            return already_cloned; 
        } else {
            if (t === "object") { // create new object
                new_obj = new obj.constructor();
            } else { // Just use functions as is
                new_obj = obj;
            }

            clones.push([obj, new_obj]); // keep track of objects we've cloned

            for (key in obj) { // clone object members
                if (obj.hasOwnProperty(key)) {
                    new_obj[key] = clone(obj[key], clones);
                }
            }
        }
    }
    return new_obj || obj;
}

Zyklischer Array-Test ...

a = []
a.push("b", "c", a)
aa = clone(a)
aa === a //=> false
aa[2] === a //=> false
aa[2] === a[2] //=> false
aa[2] === aa //=> true

Funktionstest...

f = new Function
f.a = a
ff = clone(f)
ff === f //=> true
ff.a === a //=> false

Die effiziente Möglichkeit, ein Objekt in einer Codezeile zu klonen (nicht tief zu klonen)

Eine Object.assign Methode ist Teil des Standards ECMAScript 2015 (ES6) und erfüllt genau das, was Sie benötigen.

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

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

Object.assign

Die Polyfill zur Unterstützung älterer Browser:

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

Crockford schlägt vor (und ich bevorzuge), diese Funktion zu verwenden:

function object(o) {
    function F() {}
    F.prototype = o;
    return new F();
}

var newObject = object(oldObject);

Es ist knapp, funktioniert wie erwartet und Sie brauchen keine Bibliothek.

BEARBEITEN:

Dies ist ein Polyfill für Object.create, also können Sie diesen auch verwenden.

var newObject = Object.create(oldObject);

ANMERKUNG: Wenn Sie einige davon verwenden, kann es zu Problemen bei der Verwendung von Iterationen kommen hasOwnProperty. Denn createneues leeres Objekt erstellen , die erben oldObject. Es ist jedoch immer noch nützlich und praktisch, um Objekte zu klonen.

Zum Beispiel wenn oldObject.a = 5;

newObject.a; // is 5

aber:

oldObject.hasOwnProperty(a); // is true
newObject.hasOwnProperty(a); // is false

Wenn Sie es verwenden, verfügt die Bibliothek Underscore.js über eine clone .

var newObject = _.clone(oldObject);

Ich habe zwei gute Antworten, je nachdem, ob Sie ein "einfaches altes JavaScript-Objekt" klonen möchten oder nicht.

Nehmen wir auch an, dass Sie einen vollständigen Klon ohne Prototypreferenzen auf das Quellobjekt erstellen möchten. Wenn Sie nicht an einem vollständigen Klon interessiert sind, können Sie viele der Object.clone () - Routinen verwenden, die in einigen der anderen Antworten enthalten sind (Crockfords Muster).

Bei einfachen alten JavaScript-Objekten ist ein bewährter Weg, ein Objekt in modernen Laufzeiten zu klonen, einfach:

var clone = JSON.parse(JSON.stringify(obj));

Beachten Sie, dass das Quellobjekt ein reines JSON-Objekt sein muss. Das heißt, alle geschachtelten Eigenschaften müssen Skalare sein (wie boolean, string, array, object usw.). Funktionen oder spezielle Objekte wie RegExp oder Date werden nicht geklont.

Ist es effizient? Oh Ja. Wir haben alle möglichen Klonmethoden ausprobiert und dies funktioniert am besten. Ich bin sicher, ein Ninja könnte eine schnellere Methode heraufbeschwören. Ich vermute aber, dass wir über geringfügige Gewinne sprechen.

Dieser Ansatz ist einfach und leicht zu implementieren. Wickeln Sie es in eine Komfortfunktion und wenn Sie wirklich etwas Gewinn herausholen müssen, gehen Sie zu einem späteren Zeitpunkt.

Für nicht-einfache JavaScript-Objekte gibt es keine wirklich einfache Antwort. In der Tat kann es aufgrund der dynamischen Natur von JavaScript-Funktionen und des inneren Objektzustands nicht sein. Um eine JSON-Struktur mit Funktionen zu klonen, müssen Sie diese Funktionen und ihren inneren Kontext neu erstellen. JavaScript hat dafür einfach keine standardisierte Methode.

Der korrekte Weg, dies noch einmal zu tun, ist eine bequeme Methode, die Sie innerhalb Ihres Codes deklarieren und wiederverwenden. Die Komfortmethode kann mit einem gewissen Verständnis Ihrer eigenen Objekte ausgestattet werden, sodass Sie sicherstellen können, dass das Diagramm innerhalb des neuen Objekts ordnungsgemäß neu erstellt wird.

Wir sind unsere eigenen, aber der beste allgemeine Ansatz, den ich je gesehen habe, wird hier behandelt:

http://davidwalsh.name/javascript-clone

Das ist die richtige Idee. Der Autor (David Walsh) hat das Klonen allgemeiner Funktionen auskommentiert. Dies können Sie je nach Anwendungsfall wählen.

Die Hauptidee ist, dass Sie die Instantiierung Ihrer Funktionen (oder sozusagen prototypischen Klassen) auf Typebene speziell behandeln müssen. Hier hat er einige Beispiele für RegExp und Date gegeben.

Dieser Code ist nicht nur kurz, sondern auch sehr gut lesbar. Es ist ziemlich einfach zu erweitern.

Ist das effizient? Oh Ja. Da das Ziel darin besteht, einen echten Klon mit tiefen Kopien zu erstellen, müssen Sie die Elemente des Quellobjektdiagramms durchlaufen. Mit diesem Ansatz können Sie genau festlegen, welche untergeordneten Elemente behandelt werden sollen und wie benutzerdefinierte Typen manuell behandelt werden.

Hier bitteschön. Zwei Ansätze. Beide sind meiner Ansicht nach effizient.


Überprüfen Sie diesen Benchmark: http://jsben.ch/#/bWfk9

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

JSON.parse(JSON.stringify(obj))

um die schnellste Möglichkeit zu sein, ein Objekt tief zu klonen ( jQuery.extend mit 10-20% auf true gesetzt).

jQuery.extend ist ziemlich schnell, wenn das tiefe Flag auf false (shallow clone) gesetzt ist. Dies ist eine gute Option, da sie einige zusätzliche Logik für die Typüberprüfung enthält und keine undefinierten Eigenschaften usw. kopiert. Dies wird Sie jedoch etwas verlangsamen.

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

Wenn Sie versuchen, eine bekannte Objektstruktur in einer Hot-Loop zu klonen, können Sie VIEL VIEL MEHR LEISTUNG erhalten, indem Sie einfach die Klonprozedur einbetten und das Objekt manuell erstellen.

JavaScript-Ablaufverfolgungs-Engines saugen bei der Optimierung for..in Schleifen und der Überprüfung von hasOwnProperty auch Sie langsamer. Manueller Klon, wenn Geschwindigkeit ein absolutes Muss ist.

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

Verwenden Sie die JSON.parse(JSON.stringify(obj)) -Methode für Date Objekte. JSON.stringify(new Date()) gibt eine Zeichenfolgendarstellung des Datums im ISO-Format zurück, die JSON.parse() nicht zurück konvertiert zu einem Date Objekt. Weitere Informationen finden Sie in dieser Antwort .

Beachten Sie außerdem, dass zumindest in Chrome 65 das native Klonen nicht der richtige Weg ist. Laut dieser JSPerf ist das native Klonen mit dem Erstellen einer neuen Funktion fast 800x langsamer als mit JSON.stringify, was auf der ganzen Welt unglaublich schnell ist.


Im Folgenden werden zwei Instanzen desselben Objekts erstellt. Ich habe es gefunden und benutze es derzeit. Es ist einfach und leicht zu bedienen.

var objToCreate = JSON.parse(JSON.stringify(cloneThis));

Tiefe Kopie nach Leistung: Bestnoten bis Schlechteste

  • Neuzuordnung "=" (nur String-Arrays, Zahlen-Arrays)
  • Slice (nur String-Arrays, nur Zahlen-Arrays)
  • Verkettung (nur String-Arrays, Zahlen-Arrays)
  • Benutzerdefinierte Funktion: für die Schleife oder rekursive Kopie
  • jQuery's $ .extend
  • JSON.parse (nur String-Arrays, Zahlen-Arrays, Objekt-Arrays)
  • _.Clone von Underscore.js (nur String-Arrays, Zahlen-Arrays)
  • Lo-Dash's _.cloneDeep

Kopieren Sie ein Array von Strings oder Zahlen (eine Ebene - keine Referenzzeiger) tief:

Wenn ein Array Zahlen und Strings enthält - Funktionen wie .slice (), .concat (), .splice (), der Zuweisungsoperator "=" und die Klonfunktion von Underscore.js; erstellt eine tiefe Kopie der Elemente des Arrays.

Wo die Neuzuweisung die schnellste Leistung aufweist:

var arr1 = ['a', 'b', 'c'];
var arr2 = arr1;
arr1 = ['a', 'b', 'c'];

Und .slice () hat eine bessere Leistung als .concat (), http://jsperf.com/duplicate-array-slice-vs-concat/3

var arr1 = ['a', 'b', 'c'];  // Becomes arr1 = ['a', 'b', 'c']
var arr2a = arr1.slice(0);   // Becomes arr2a = ['a', 'b', 'c'] - deep copy
var arr2b = arr1.concat();   // Becomes arr2b = ['a', 'b', 'c'] - deep copy

Kopieren Sie ein Array von Objekten tief (zwei oder mehr Ebenen - Referenzzeiger):

var arr1 = [{object:'a'}, {object:'b'}];

Schreiben Sie eine benutzerdefinierte Funktion (hat eine schnellere Leistung als $ .extend () oder JSON.parse):

function copy(o) {
   var out, v, key;
   out = Array.isArray(o) ? [] : {};
   for (key in o) {
       v = o[key];
       out[key] = (typeof v === "object" && v !== null) ? copy(v) : v;
   }
   return out;
}

copy(arr1);

Verwenden Sie Dienstprogramme von Drittanbietern:

$.extend(true, [], arr1); // Jquery Extend
JSON.parse(arr1);
_.cloneDeep(arr1); // Lo-dash

Wo jQuery's $ .extend eine bessere Leistung bringt:


Strukturiertes Klonen

HTML5 definiert einen internen "strukturierten" Klonierungsalgorithmus , der tiefe Klone von Objekten erstellen kann. Es ist immer noch auf bestimmte integrierte Typen beschränkt, aber zusätzlich zu den wenigen von JSON unterstützten Typen unterstützt es auch Datumsangaben, RegExps, Maps, Sets, Blobs, FileLists, ImageDatas, spärliche Arrays, typisierte Arrays und möglicherweise mehr . Außerdem werden Referenzen innerhalb der geklonten Daten beibehalten, sodass zyklische und rekursive Strukturen unterstützt werden können, die zu Fehlern bei JSON führen würden.

Direkter Support in Browsern: In Kürze verfügbar? 🙂

Browser bieten derzeit keine direkte Schnittstelle für den strukturierten Klonierungsalgorithmus, aber eine globale structuredClone() Funktion wird in whatwg / html # 793 auf GitHub aktiv diskutiert und wird möglicherweise in Kürze erscheinen! Wie derzeit vorgeschlagen, ist die Verwendung für die meisten Zwecke so einfach wie:

const clone = structuredClone(original);

Bis zur Auslieferung werden die strukturierten Klonimplementierungen von Browsern nur indirekt angezeigt.

Asynchrone Problemumgehung: Verwendbar. 😕

Der geringere Aufwand zum Erstellen eines strukturierten Klons mit vorhandenen APIs besteht darin, die Daten über einen Port eines MessageChannels . Der andere Port gibt ein message mit einem strukturierten Klon der 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();

Synchronous Workarounds: Schrecklich! 🤢

Es gibt keine guten Optionen, um strukturierte Klone synchron zu erstellen. Hier sind ein paar unpraktische Hacks.

history.pushState() und history.replaceState() erstellen einen strukturierten Klon des ersten Arguments und weisen diesen Wert history.state . Sie können dies verwenden, um einen strukturierten Klon eines beliebigen Objekts wie folgt zu 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();

Obwohl synchron, kann dies extrem langsam sein. Es verursacht den gesamten Aufwand für die Bearbeitung des Browserverlaufs. Ein wiederholtes Aufrufen dieser Methode kann dazu führen, dass Chrome vorübergehend nicht mehr reagiert.

Der Notification erstellt einen strukturierten Klon der zugehörigen Daten. Es wird auch versucht, eine Browserbenachrichtigung für den Benutzer anzuzeigen, dies schlägt jedoch automatisch fehl, wenn Sie keine Benachrichtigungsberechtigung angefordert haben. Falls Sie die Erlaubnis für andere Zwecke haben, schließen wir die von uns erstellte Benachrichtigung umgehend.

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


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

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

Hier ist eine umfassende clone () -Methode, die jedes JavaScript-Objekt klonen kann. Es behandelt fast alle Fälle:

function clone(src, deep) {

    var toString = Object.prototype.toString;
    if (!src && typeof src != "object") {
        // Any non-object (Boolean, String, Number), null, undefined, NaN
        return src;
    }

    // Honor native/custom clone methods
    if (src.clone && toString.call(src.clone) == "[object Function]") {
        return src.clone(deep);
    }

    // DOM elements
    if (src.nodeType && toString.call(src.cloneNode) == "[object Function]") {
        return src.cloneNode(deep);
    }

    // Date
    if (toString.call(src) == "[object Date]") {
        return new Date(src.getTime());
    }

    // RegExp
    if (toString.call(src) == "[object RegExp]") {
        return new RegExp(src);
    }

    // Function
    if (toString.call(src) == "[object Function]") {

        //Wrap in another method to make sure == is not true;
        //Note: Huge performance issue due to closures, comment this :)
        return (function(){
            src.apply(this, arguments);
        });
    }

    var ret, index;
    //Array
    if (toString.call(src) == "[object Array]") {
        //[].slice(0) would soft clone
        ret = src.slice();
        if (deep) {
            index = ret.length;
            while (index--) {
                ret[index] = clone(ret[index], true);
            }
        }
    }
    //Object
    else {
        ret = src.constructor ? new src.constructor() : {};
        for (var prop in src) {
            ret[prop] = deep
                ? clone(src[prop], true)
                : src[prop];
        }
    }
    return ret;
};

Ich weiß, dass dies ein alter Beitrag ist, aber ich dachte, das könnte der nächsten Person, die dahinter stolpert, helfen.

Solange Sie keinem Objekt ein Objekt zuweisen, behält es keinen Verweis im Speicher. Um ein Objekt zu erstellen, das Sie für andere Objekte freigeben möchten, müssen Sie eine Fabrik wie folgt erstellen:

var a = function(){
    return {
        father:'zacharias'
    };
},
b = a(),
c = a();
c.father = 'johndoe';
alert(b.father);

Es scheint noch keinen idealen Deep-Clone-Operator für Array-ähnliche Objekte zu geben. Wie der folgende Code veranschaulicht, wandelt der jQuery-Cloner von John Resig Arrays mit nicht-numerischen Eigenschaften in Objekte um, die keine Arrays sind, und der JSON-Cloner von RegDwight löscht die nicht-numerischen Eigenschaften. Die folgenden Tests veranschaulichen diese Punkte in mehreren Browsern:

function jQueryClone(obj) {
   return jQuery.extend(true, {}, obj)
}

function JSONClone(obj) {
   return JSON.parse(JSON.stringify(obj))
}

var arrayLikeObj = [[1, "a", "b"], [2, "b", "a"]];
arrayLikeObj.names = ["m", "n", "o"];
var JSONCopy = JSONClone(arrayLikeObj);
var jQueryCopy = jQueryClone(arrayLikeObj);

alert("Is arrayLikeObj an array instance?" + (arrayLikeObj instanceof Array) +
      "\nIs the jQueryClone an array instance? " + (jQueryCopy instanceof Array) +
      "\nWhat are the arrayLikeObj names? " + arrayLikeObj.names +
      "\nAnd what are the JSONClone names? " + JSONCopy.names)

Wenn es keinen eingebauten gibt, können Sie Folgendes 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;
}




clone