3750 Stimmen

Wie kann ich ein JavaScript-Objekt korrekt klonen?

Ich habe ein Objekt x . Ich würde es gerne als Objekt kopieren y , so dass Änderungen an y nicht verändern x . Ich habe festgestellt, dass das Kopieren von Objekten, die von integrierten JavaScript-Objekten abgeleitet sind, zu zusätzlichen, unerwünschten Eigenschaften führt. Das ist kein Problem, da ich eines meiner eigenen, wörtlich konstruierten Objekte kopiere.

Wie kann ich ein JavaScript-Objekt korrekt klonen?

35 Stimmen

0 Stimmen

Unterstützen Sie auf jeden Fall @Niyaz! Shortlink: tinyurl.com/JSCopyObject

290 Stimmen

Für JSON verwende ich mObj=JSON.parse(JSON.stringify(jsonObject));

9voto

rela589n Punkte 508

Das korrekteste Objekt zum Kopieren ist die Verwendung von Object.create :

Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));

Eine solche Notation macht identisch das gleiche Objekt mit korrektem Prototyp und versteckten Eigenschaften.

0 Stimmen

Ja, aber es kommt darauf an, was Sie wollen (vielleicht wollen Sie die Stütze einfach "kopieren") Werte , pas die eigentlichen Prop-Deskriptor-Referenzen...), und je nach Quellobjekt benötigen Sie möglicherweise eine Object.assign um auch enumarable Eigenschaften zu erhalten (d.h. einfache alte Schlüssel-Wert-Paare, die auf das Objekt gesetzt werden, unabhängig von Prototyp und "dynamischen" beschriebenen Requisiten).

8voto

Radu Simionescu Punkte 4154

Dies ist eine Anpassung des Codes von A. Levy, um auch das Klonen von Funktionen und mehrfachen/zyklischen Referenzen zu behandeln - was bedeutet, dass, wenn zwei Eigenschaften in dem Baum, der geklont wird, Referenzen desselben Objekts sind, der geklonte Objektbaum diese Eigenschaften auf ein und denselben Klon des referenzierten Objekts zeigen lässt. Damit wird auch der Fall der zyklischen Abhängigkeiten gelöst, der, wenn er nicht behandelt wird, zu einer Endlosschleife führt. Die Komplexität des Algorithmus ist O(n)

function clone(obj){
    var clonedObjectsArray = [];
    var originalObjectsArray = []; //used to remove the unique ids when finished
    var next_objid = 0;

    function objectId(obj) {
        if (obj == null) return null;
        if (obj.__obj_id == undefined){
            obj.__obj_id = next_objid++;
            originalObjectsArray[obj.__obj_id] = obj;
        }
        return obj.__obj_id;
    }

    function cloneRecursive(obj) {
        if (null == obj || typeof obj == "string" || typeof obj == "number" || typeof obj == "boolean") return obj;

        // Handle Date
        if (obj instanceof Date) {
            var copy = new Date();
            copy.setTime(obj.getTime());
            return copy;
        }

        // Handle Array
        if (obj instanceof Array) {
            var copy = [];
            for (var i = 0; i < obj.length; ++i) {
                copy[i] = cloneRecursive(obj[i]);
            }
            return copy;
        }

        // Handle Object
        if (obj instanceof Object) {
            if (clonedObjectsArray[objectId(obj)] != undefined)
                return clonedObjectsArray[objectId(obj)];

            var copy;
            if (obj instanceof Function)//Handle Function
                copy = function(){return obj.apply(this, arguments);};
            else
                copy = {};

            clonedObjectsArray[objectId(obj)] = copy;

            for (var attr in obj)
                if (attr != "__obj_id" && obj.hasOwnProperty(attr))
                    copy[attr] = cloneRecursive(obj[attr]);                 

            return copy;
        }       

        throw new Error("Unable to copy obj! Its type isn't supported.");
    }
    var cloneObj = cloneRecursive(obj);

    //remove the unique ids
    for (var i = 0; i < originalObjectsArray.length; i++)
    {
        delete originalObjectsArray[i].__obj_id;
    };

    return cloneObj;
}

Einige schnelle Tests

var auxobj = {
    prop1 : "prop1 aux val", 
    prop2 : ["prop2 item1", "prop2 item2"]
    };

var obj = new Object();
obj.prop1 = "prop1_value";
obj.prop2 = [auxobj, auxobj, "some extra val", undefined];
obj.nr = 3465;
obj.bool = true;

obj.f1 = function (){
    this.prop1 = "prop1 val changed by f1";
};

objclone = clone(obj);

//some tests i've made
console.log("test number, boolean and string cloning: " + (objclone.prop1 == obj.prop1 && objclone.nr == obj.nr && objclone.bool == obj.bool));

objclone.f1();
console.log("test function cloning 1: " + (objclone.prop1 == 'prop1 val changed by f1'));
objclone.f1.prop = 'some prop';
console.log("test function cloning 2: " + (obj.f1.prop == undefined));

objclone.prop2[0].prop1 = "prop1 aux val NEW";
console.log("test multiple references cloning 1: " + (objclone.prop2[1].prop1 == objclone.prop2[0].prop1));
console.log("test multiple references cloning 2: " + (objclone.prop2[1].prop1 != obj.prop2[0].prop1));

2 Stimmen

Ab September 2016 ist dies die nur die richtige Lösung der Frage.

6voto

yazjisuhail Punkte 357

Ich habe meine eigene Implementierung geschrieben. Ich bin mir nicht sicher, ob das eine bessere Lösung ist:

/*
    a function for deep cloning objects that contains other nested objects and circular structures.
    objects are stored in a 3D array, according to their length (number of properties) and their depth in the original object.
                                    index (z)
                                         |
                                         |
                                         |
                                         |
                                         |
                                         |                      depth (x)
                                         |_ _ _ _ _ _ _ _ _ _ _ _
                                        /_/_/_/_/_/_/_/_/_/
                                       /_/_/_/_/_/_/_/_/_/
                                      /_/_/_/_/_/_/...../
                                     /................./
                                    /.....            /
                                   /                 /
                                  /------------------
            object length (y)    /
*/

Es folgt die Umsetzung:

function deepClone(obj) {
    var depth = -1;
    var arr = [];
    return clone(obj, arr, depth);
}

/**
 *
 * @param obj source object
 * @param arr 3D array to store the references to objects
 * @param depth depth of the current object relative to the passed 'obj'
 * @returns {*}
 */
function clone(obj, arr, depth){
    if (typeof obj !== "object") {
        return obj;
    }

    var length = Object.keys(obj).length; // native method to get the number of properties in 'obj'

    var result = Object.create(Object.getPrototypeOf(obj)); // inherit the prototype of the original object
    if(result instanceof Array){
        result.length = length;
    }

    depth++; // depth is increased because we entered an object here

    arr[depth] = []; // this is the x-axis, each index here is the depth
    arr[depth][length] = []; // this is the y-axis, each index is the length of the object (aka number of props)
    // start the depth at current and go down, cyclic structures won't form on depths more than the current one
    for(var x = depth; x >= 0; x--){
        // loop only if the array at this depth and length already have elements
        if(arr[x][length]){
            for(var index = 0; index < arr[x][length].length; index++){
                if(obj === arr[x][length][index]){
                    return obj;
                }
            }
        }
    }

    arr[depth][length].push(obj); // store the object in the array at the current depth and length
    for (var prop in obj) {
        if (obj.hasOwnProperty(prop)) result[prop] = clone(obj[prop], arr, depth);
    }

    return result;
}

0 Stimmen

Bei meinem Objekt nicht funktioniert, obwohl mein Fall ein wenig komplex ist.

6voto

chickens Punkte 14182

Einheimisches JS:

const shallowClone = {...originalObj};
const deepClone = JSON.parse(JSON.stringify(originalObj));

Bibliotheken nutzen:

// Lodash
const shallowClone = _.clone(originalObj);
const deepClone = _. cloneDeep(originalObj);

// JQuery
const shallowClone = jQuery.extend({}, originalObj);
const deepClone = jQuery.extend(true, {}, originalObj);

// Angular
const deepClone = angular.copy(originalObj);

6voto

heinob Punkte 18537

Ich wollte nur hinzufügen, dass alle Object.create Lösungen in diesem Beitrag, dass dies nicht in der gewünschten Weise mit Nodejs funktioniert.

In Firefox wird das Ergebnis von

var a = {"test":"test"};
var b = Object.create(a);
console.log(b);´

es

{test:"test"} .

In nodejs ist es

{}

0 Stimmen

Dies ist eine prototypische Vererbung, kein Klonen.

1 Stimmen

@d13 während Ihr Argument ist gültig, beachten Sie, dass es keine standardisierte Möglichkeit in JavaScript, ein Objekt zu klonen. Dies ist eine prototypische Vererbung, kann aber trotzdem als Klon verwendet werden, wenn man die Konzepte versteht.

0 Stimmen

@froginvasion. Das einzige Problem bei der Verwendung von Object.create ist, dass verschachtelte Objekte und Arrays nur Zeigerreferenzen auf die verschachtelten Objekte und Arrays des Prototyps sind. jsbin.com/EKivInO/2/edit?js,console . Technisch gesehen sollte ein "geklontes" Objekt seine eigenen eindeutigen Eigenschaften haben, die keine gemeinsamen Verweise auf Eigenschaften anderer Objekte sind.

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