Frage

Was ist der effizienteste Weg, um ein JavaScript-Objekt zu klonen? Ich habe obj = eval(uneval(o)); gesehen verwendet wird, aber das ist nicht-Standard und 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, keine kanonische Lösung vorhanden ist.

War es hilfreich?

Lösung

  

2019-Juni Hinweis: Dies war ursprünglich eine Antwort auf eine andere Antwort, keine richtige Antwort auf diese Frage. Keine Ahnung, warum es wurde als die richtige Antwort ausgewählt. Da aber die upvotes lawinenartig und es ist bei weitem der # 1 Antwort auf diese Frage, wird es die Lösungen als Wiki Antwort zusammenfassen.

Native tief Klonen

Es ist „strukturiertes Klonen“ genannt, arbeitet experimentell in Knoten 11 und später, und hoffentlich in Browsern landen wird. Siehe diese beantworten für weitere Details.

Fast Klonen mit Datenverlust - JSON.parse / stringify

Wenn Sie nicht verwenden Dates, Funktionen, undefined, Infinity, regexps, Karten, Sets, Blobs, Dateilisten, ImageDatas, Sparse-Matrizen, typisiert Arrays oder andere komplexe Typen in Ihrem Objekt, ein sehr einfacher Einzeiler zu tiefer Klon einer Objekt:

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

Siehe cloneDeep ; kann separat lodash.clonedeep -Modul über die und wahrscheinlich die beste Wahl, wenn Sie importiert werden‘ re nicht bereits eine Bibliothek, die eine tiefe Klonfunktion liefert

  • Angular - angular.copy
  • jQuery - jQuery.extend(true, { }, oldObject) ; .clone() nur Klone DOM-Elemente
  • ES6

    Für die Vollständigkeit, beachten Sie, dass ES6 zwei flache Kopie Mechanismen bietet: Object.assign() und der Verbreitung Operator .

    Andere Tipps

    Zur Kasse dieser Benchmark: http://jsben.ch/#/bWfk9

    In meinem vorherigen Test, bei denen Geschwindigkeit ein Hauptanliegen war, fand ich

    JSON.parse(JSON.stringify(obj))
    

    der langsamste Weg zu tiefer Klon sein ein Objekt (es ist langsamer als jQuery.extend mit deep Flag gesetzt wahr um 10-20%).

    jQuery.extend ziemlich schnell ist, wenn der deep-Flag gesetzt ist auf false (flache Klon). Es ist eine gute Wahl, weil es einige zusätzliche Logik für die Art Validierung umfasst und nicht über undefinierte Eigenschaften kopiert, usw., aber das wird man auch langsam nach unten ein wenig.

    Wenn Sie wissen, die Struktur der Objekte, die Sie zu klonen versuchen oder tief verschachtelten Arrays vermeiden Sie eine einfache for (var i in obj) Schleife schreiben können Ihr Objekt zu klonen, während hasOwnProperty prüft und es wird viel viel schneller als jQuery.

    Schließlich, wenn Sie eine bekannte Objektstruktur in einer heißen Schleife zu klonen versuchen Sie viel viel mehr PERFORMANCE einfach in-Auskleidung der Klon Verfahren erhalten können und das Objekt manuell zu konstruieren.

    JavaScript Spur Motoren saugen an for..in Schleifen Optimierung und Kontrolle hasOwnProperty werden Sie auch verlangsamen. Manuelles Klon, wenn die Geschwindigkeit ist ein absolutes Muss.

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

    Vorsicht vor der JSON.parse(JSON.stringify(obj)) Methode auf Date Objekten mit - JSON.stringify(new Date()) gibt eine Zeichenfolge Darstellung des Datums im ISO-Format, die JSON.parse() nicht konvertieren zurück zu einem Date Objekt. Sehen Sie diese Antwort für weitere Details .

    Zusätzlich Bitte beachten Sie, dass in Chrome 65 zumindest native Klonen ist nicht der Weg zu gehen. Nach dieser JSPerf , nativen Klonen Durchführung durch eine neue Funktion zu schaffen ist fast 800x langsamer als JSON.stringify verwendet, die unglaublich schnell den ganzen Weg auf der ganzen Linie ist.

    Update für ES6

    Wenn Sie mit Javascript ES6 versuchen, diese native Methode für das Klonen oder flache Kopie.

    Object.assign({}, obj);
    

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

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

    Strukturierte Cloning

    Der HTML-Standard enthält eine interne strukturierte Klonen / Serialisierung Algorithmus das kann tief Klonen von Objekten erstellen. Es ist immer noch beschränkt auf bestimmte Typen-in gebaut, aber zusätzlich zu den wenigen Arten unterstützt von JSON unterstützt es auch Termine, regexps, Karten, Sets, Blobs, Dateilisten, ImageDatas, Sparse-Matrizen, typisiert Arrays, und wahrscheinlich in der Zukunft . Es bewahrt auch Verweise innerhalb der geklonten Daten, so dass sie zyklisch und rekursive Strukturen zu unterstützen, die Fehler für JSON verursachen würde.

    Support in Node.js: Experimental

    Wenn es keine builtin war, 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;
    }
    

    Die effiziente Art und Weise zu klonen (nicht tief Klon) ein Objekt in einer Zeile Code

    Ein Object.assign Verfahren ist ein Teil von die ECMAScript 2015 (ES6) Standard und macht genau das, was Sie brauchen.

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

    Die Object.assign () Methode wird verwendet, um die Werte aller enumerable eigenen Eigenschaften von einem oder mehreren Quellobjekte zu einem Zielobjekt zu kopieren.

    Lesen Sie mehr ...

    Die polyfill ältere Browser 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;
        }
      });
    }
    

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

    Test:

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

    Dies ist, was ich verwende:

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

    Deep Kopie durch Leistung: Auf Rang vom besten zum schlechtesten

    • Neuzuordnung "=" (String-Arrays, die Anzahl Arrays - nur)
    • Scheibe (String-Arrays, Zahlen-Arrays - nur)
    • Verkettungs (String-Arrays, die Anzahl Arrays - nur)
    • Benutzerdefinierte Funktion: for-Schleife oder rekursive Kopie
    • jQuery $ .extend
    • JSON.parse (String-Arrays, die Anzahl Arrays, Objekt-Arrays - nur)
    • Underscore.js 's _.clone (String-Arrays, Zahlen-Arrays - nur )
    • Lo-Dash _.cloneDeep

    Deep Kopie ein Array von Strings oder Zahlen (eine Ebene - keine Referenzzeiger):

    Wenn ein Array enthält Zahlen und Strings - Funktionen wie .slice (), .concat (), .splice (), den Zuweisungsoperator "=" und Underscore.js des Klons Funktion; wird eine tiefe Kopie der Elemente des Arrays machen.

    Wo Neuzuordnung hat die schnellste Leistung:

    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
    

    Deep Kopie ein Array von Objekten (zwei oder mehr Ebenen - Referenzzeiger):

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

    Schreiben Sie eine benutzerdefinierte Funktion (hat 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);
    

    Verwendung von Drittanbieter-Dienstprogramm Funktionen:

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

    Wo jQuery $ .extend hat eine bessere Leistung:

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

    Es gibt eine Bibliothek (genannt „Klon“) , dass dies ganz gut tut. Es bietet die vollständigste rekursive Klonen / Kopieren von beliebigen Objekten, die ich kenne. Es unterstützt auch zirkuläre Referenzen, die nicht von den anderen Antworten bedeckt ist, vor.

    Sie können finden rel="noreferrer"> auch. Es kann für den Browser sowie Node.js verwendet werden.

    Hier ist ein Beispiel, wie man es verwenden:

    Installieren Sie es mit

    npm install clone
    

    oder verpacken sie mit Ender .

    ender build clone [...]
    

    Sie können auch den Quellcode 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' } }
    

    (Disclaimer:. Ich bin der Autor der Bibliothek)

    Ich weiß, das ist eine alte Post, aber ich dachte, das an die nächste Person eine Hilfe sein, die entlang stolpert.

    Solange Sie zuweisen ein Objekt nicht zu irgendetwas es keinen Hinweis im Speicher hält. So ein Objekt machen, die Sie unter anderem Objekte teilen möchten, werden Sie eine Fabrik wie so erstellen müssen:

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

    Cloning ein Objekt war immer ein Problem in JS, aber es war alles über vor ES6, liste ich verschiedene Arten des Kopierens eines Objekts in JavaScript unten, stellen Sie sich das Objekt unten und möchte eine tiefe Kopie davon haben:

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

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

    1) ES5 +, eine einfache Funktion Verwenden Sie die Kopie zu tun:

    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) Underscore.js & Loadash:

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

    diese Hilfe Hoffnung ...

    Wenn Sie verwenden es, die Underscore.js Bibliothek hat eine Klon Methode.

    var newObject = _.clone(oldObject);
    

    Deep Kopieren von Objekten in JavaScript (Ich denke, die beste und einfachste)

    1. Mit JSON.parse (JSON.stringify (Objekt));

    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.Using erstellt 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. Mit Lo-Dash _.cloneDeep Link lodash

    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. Mit 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, wenn

    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.Using 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, wenn

    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.)
    

    Referenz medium.com

    JSBEN.CH Performance Benchmarking Spielplatz 1 ~ 3 http://jsben.ch/KVQLd Performance Tief Kopieren von Objekten in JavaScript

    Hier ist eine Version von ConroyP Antwort über das funktioniert auch, wenn der Konstruktor Parameter hat erforderlich:

    //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.

    Edit:

    Hier ist eine robustere Version (dank Justin McCandless dies nun zyklische Referenzen als auch 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://stackoverflow.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();
        };
    }
    

    In der folgenden erstellt zwei Instanzen desselben Objekts. Ich fand es und bin mit ihm zur Zeit. Es ist einfach und leicht zu bedienen.

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

    Lodash hat eine schöne _.cloneDeep (Wert) Methode:

    var objects = [{ 'a': 1 }, { 'b': 2 }];
    
    var deep = _.cloneDeep(objects);
    console.log(deep[0] === objects[0]);
    // => false
    
    function clone(obj)
     { var clone = {};
       clone.prototype = obj.prototype;
       for (property in obj) clone[property] = obj[property];
       return clone;
     }
    

    Crockford schlägt vor (und ich ziehe) mit dieser Funktion:

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

    Es ist kurz und bündig, wie erwartet funktioniert, und Sie brauchen nicht eine Bibliothek.


    EDIT:

    Dies ist ein polyfill für Object.create, so können Sie auch diese verwenden.

    var newObject = Object.create(oldObject);
    

    Hinweis: Wenn Sie einige dieser verwenden, können Sie Probleme mit einigen Iteration haben die hasOwnProperty verwenden. Denn create neues leeres Objekt erstellen, die oldObject erbt. Aber es ist immer noch nützlich und praktisch für Objekte klonen.

    Für exemple wenn oldObject.a = 5;

    newObject.a; // is 5
    

    aber:

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

    Shallow Kopie Einzeiler ( ECMAScript 5th edition ):

    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
    

    Und flache Kopie Einzeiler ( 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
    

    Nur weil ich nicht sehen AngularJS erwähnt und dachte, dass die Leute vielleicht wissen wollen. ..

    angular.copy stellt auch ein Verfahren der tiefen Kopieren von Objekten und Arrays.

    Es scheint kein Ideal tiefe Klon Betreiber noch für Array-ähnliche Objekte. Wie Sie den Code unten zeigt, wird John Resig jQuery cloner Arrays mit nicht-numerischen Eigenschaften in Objekte, die keine Arrays sind, und RegDwight JSON cloner die nicht-numerischen Eigenschaften fällt. Die folgenden Tests zeigen diese Punkte auf 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)
    

    Ich habe zwei gute Antworten je nachdem, ob Ihr Ziel ist es, ein „plain old JavaScript-Objekt“ oder nicht zu klonen.

    Lassen Sie sich auch davon ausgehen, dass Ihre Absicht ist es, eine vollständige Klon ohne Prototyp Referenzen zurück zu dem Quellobjekt zu erstellen. Wenn Sie nicht daran interessiert, in einem vollständigen Klon sind, dann können Sie in einigen der anderen Antworten (Crockford Muster), die viele der Object.clone () Routinen verwenden.

    Für die guten alte JavaScript-Objekte, eine bewährte und gute Möglichkeit, ein Objekt in der modernen Runtimes zu klonen ist ganz einfach:

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

    Beachten Sie, dass das Quellobjekt muss ein reines JSON-Objekt sein. Das heißt, alle seine verschachtelten Eigenschaften müssen Skalare sein (wie boolean, String, Array, Objekt, etc). Alle Funktionen, oder spezielle Objekte wie RegExp oder Datum werden nicht geklont werden.

    Ist es leistungsfähig? Oh Ja. Wir haben alle Arten von Klonen Methoden ausprobiert und dies am besten funktioniert. Ich bin sicher, einige ninja eine schnellere Methode heraufbeschwören könnte. Aber ich vermute, wir reden über marginale Gewinne.

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

    Nun, für Nicht-Ebene JavaScript-Objekte, ist es nicht eine wirklich einfache Antwort. In der Tat kann es nicht wegen der Dynamik der JavaScript-Funktionen und inneren Objektzustandes. Tief Klonen eine JSON-Struktur mit Funktionen innerhalb erfordert, dass Sie die Funktionen und ihren inneren Kontext neu zu erstellen. Und Javascript einfach nicht über eine standardisierte Art und Weise, dies zu tun.

    Der richtige Weg, dies zu tun, noch einmal, ist über eine bequeme Methode, die Sie in Ihrem Code deklarieren und wiederzuverwenden. Die bequeme Methode kann mit einem gewissen Verständnis für Ihre eigenen Objekte ausgestattet sein, so dass Sie sicherstellen können, um richtig die Grafik innerhalb des neuen Objekts neu zu erstellen.

    Wir sind unsere eigenen geschrieben, aber die beste allgemeine Ansatz, den ich habe hier behandelt zu sehen ist:

    http://davidwalsh.name/javascript-clone

    Dies ist die richtige Idee. Der Autor (David Walsh) hat das Klonen von verallgemeinerten Funktionen Kommentar gesetzt. Dies ist etwas, das Sie je nach Anwendungsfall tun könnten wählen.

    Die Grundidee ist, dass Sie die Instanziierung Ihrer Funktionen (oder prototypal Klassen sozusagen) auf einer Pro-Typ Basis spezielle Griff benötigen. Hier ist er ein paar Beispiele für RegExp und Datum versehen.

    Dies ist nicht nur Code kurz, aber es ist auch sehr gut lesbar. Es ist ziemlich einfach zu erweitern.

    Ist das leistungsfähig? Oh Ja. Da das Ziel ist, eine wahre Tief Kopie Klon zu produzieren, dann sind Sie gehen zu müssen, die Mitglieder des Quellobjektgraphen gehen. Mit diesem Ansatz können Sie zwicken genau, welche untergeordneten Elemente zu behandeln und wie manuell benutzerdefinierte Typen verarbeiten.

    Also los Sie. Zwei Ansätze. Beide sind effizient in meiner Ansicht.

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

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

    Zyklische 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
    

    AngularJS

    Nun, wenn Sie Winkel verwenden Sie könnten dies auch tun

    var newObject = angular.copy(oldObject);
    

    Ich bin nicht einverstanden mit der Antwort mit dem größten Stimmen hier . Ein rekursive Tief Clone ist viel schneller als der JSON.parse (JSON.stringify (obj)) Ansatz erwähnt.

    Und hier ist die Funktion zum schnellen Nachschlagen:

    function cloneDeep (o) {
      let newO
      let i
    
      if (typeof o !== 'object') return o
    
      if (!o) return o
    
      if (Object.prototype.toString.apply(o) === '[object Array]') {
        newO = []
        for (i = 0; i < o.length; i += 1) {
          newO[i] = cloneDeep(o[i])
        }
        return newO
      }
    
      newO = {}
      for (i in o) {
        if (o.hasOwnProperty(i)) {
          newO[i] = cloneDeep(o[i])
        }
      }
      return newO
    }
    
    // obj target object, vals source object
    var setVals = function (obj, vals) {
        if (obj && vals) {
            for (var x in vals) {
                if (vals.hasOwnProperty(x)) {
                    if (obj[x] && typeof vals[x] === 'object') {
                        obj[x] = setVals(obj[x], vals[x]);
                    } else {
                        obj[x] = vals[x];
                    }
                }
            }
        }
        return obj;
    };
    

    Für die Menschen, die die JSON.parse(JSON.stringify(obj)) Version verwenden möchten, ohne jedoch die Date-Objekte zu verlieren, können Sie die zweites Argument von parse Methode die Strings zurück zum Datum konvertieren:

    function clone(obj) {
      var regExp = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
      return JSON.parse(JSON.stringify(x), function(k, v) {
        if (typeof v === 'string' && regExp.test(v))
          return new Date(v);
        return v;
      });
    }
    

    Hier ist eine umfassendes Klon () Methode, die jedes JavaScript-Objekt klonen. 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;
    };
    
    Lizenziert unter: CC-BY-SA mit Zuschreibung
    Nicht verbunden mit StackOverflow
    scroll top