Domanda

Ho un oggetto, x.Vorrei copiare come oggetto y, tali cambiamenti che y non modificare x.Ho capito che la copia di oggetti derivati da oggetti JavaScript incorporati comporterà extra indesiderati o di proprietà.Questo non è un problema, dato che ho una copia di uno dei miei, letterale-oggetti costruiti.

Come faccio correttamente clonare un oggetto JavaScript?

È stato utile?

Soluzione

Per fare questo per ogni oggetto in JavaScript non essere semplice o immediato.C'è il problema dei erroneamente raccogliendo gli attributi dell'oggetto prototipo che dovrebbe essere di sinistra nel prototipo e non copiati nella nuova istanza.Se, per esempio, si sta aggiungendo un clone metodo per Object.prototype, come alcune risposte raffigurano, è necessario ignorare esplicitamente tale attributo.Ma cosa succede se ci sono altri metodi aggiunto Object.prototype, o altri intermedi prototipi, che non si conoscono?In questo caso, si può copiare gli attributi non si deve, pertanto, è necessario rilevare impreviste, non locale attributi con il hasOwnProperty metodo.

Oltre a non enumerabile attributi, si incontrano un problema più difficile quando si tenta di copiare gli oggetti che sono nascosti proprietà.Per esempio, prototype è nascosto, di proprietà di una funzione.Inoltre, un oggetto prototipo è fatto riferimento, con l'attributo __proto__, che è anche nascosto, e non possono essere copiati da un per/in loop scorrendo la fonte attributi dell'oggetto.Penso __proto__ potrebbe essere specifico per Firefox interprete JavaScript e può essere qualcosa di diverso in altri browser, ma si ottiene l'immagine.Non tutto è enumerabile.È possibile copiare un attributo nascosto se si conosce il nome, ma non so di un modo per scoprire automaticamente.

Ancora un altro intoppo nella ricerca di una soluzione elegante è un problema di impostazione del prototipo eredità correttamente.Se la vostra sorgente dell'oggetto prototipo è Object, e poi , semplicemente, la creazione di un nuovo oggetto generale con {} funzionerà, ma se la fonte del prototipo è qualche discendente di Object, allora si sta andando ad essere mancanti i membri aggiuntivi da quel prototipo che hai saltato utilizzando il hasOwnProperty filtro, o che sono stati in un prototipo, ma non enumerabile, in primo luogo.Una soluzione potrebbe essere quella di chiamare l'origine dell'oggetto constructor proprietà per ottenere la copia iniziale oggetto e quindi copiare gli attributi, ma poi ancora non sarà possibile ottenere non enumerabile attributi.Per esempio, un Date oggetto memorizza i dati come un membro nascosto:

function clone(obj) {
    if (null == obj || "object" != typeof obj) return obj;
    var copy = obj.constructor();
    for (var attr in obj) {
        if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
    }
    return copy;
}

var d1 = new Date();

/* Executes function after 5 seconds. */
setTimeout(function(){
    var d2 = clone(d1);
    alert("d1 = " + d1.toString() + "\nd2 = " + d2.toString());
}, 5000);

La stringa della data per d1 sarà di 5 secondi dietro a quello di d2.Un modo per fare una Date la stessa di un altro è chiamando il setTime il metodo, ma che è specifico per il Date classe.Io non credo che ci sia un a prova di proiettile generale per la soluzione di questo problema, anche se sarei felice di essere sbagliato!

Quando ho dovuto implementare generale una copia profonda ho finito per compromettere assumendo che ho solo bisogno di copiare un normale Object, Array, Date, String, Number, o Boolean.Gli ultimi 3 tipi immutabili, così ho potuto eseguire una copia superficiale e di non preoccuparsi di cambiare.Ho ipotizza che tutti gli elementi contenuti nel Object o Array sarebbe anche uno dei 6 tipi semplici in quella lista.Questo può essere realizzato con un codice come il seguente:

function clone(obj) {
    var copy;

    // Handle the 3 simple types, and null or undefined
    if (null == obj || "object" != typeof obj) return obj;

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

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

    // Handle Object
    if (obj instanceof Object) {
        copy = {};
        for (var attr in obj) {
            if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
        }
        return copy;
    }

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

La funzione di cui sopra funzionerà adeguatamente per il 6 tipi semplici che ho citato, come i dati negli oggetti e array forma una struttura ad albero.Che è, non c'è più di un riferimento agli stessi dati in oggetto.Per esempio:

// This would be cloneable:
var tree = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "right" : null,
    "data"  : 8
};

// This would kind-of work, but you would get 2 copies of the 
// inner node instead of 2 references to the same copy
var directedAcylicGraph = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "data"  : 8
};
directedAcyclicGraph["right"] = directedAcyclicGraph["left"];

// Cloning this would cause a stack overflow due to infinite recursion:
var cyclicGraph = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "data"  : 8
};
cyclicGraph["right"] = cyclicGraph;

Non sarà in grado di gestire qualsiasi oggetto JavaScript, ma può essere sufficiente per molti scopi, come lungo come non supporre che solo il lavoro per qualsiasi cosa ti buttare a questo.

Altri suggerimenti

Se non si utilizza Dates, funzioni, indefinito, o l'Infinito dentro l'oggetto, è molto semplice, una nave di linea è 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'
}
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()

Questo funziona per tutti i tipi di oggetti che contengono oggetti, array, stringhe, booleani e numeri.

Vedi anche questo articolo sulle strutturato clone algoritmo di browser che viene utilizzato durante l'invio dei messaggi da e per un lavoratore.Esso contiene anche una funzione per la profondità di clonazione.

Con jQuery è possibile copia superficiale con estendere:

var copiedObject = jQuery.extend({}, originalObject)

successive modifiche al copiedObject non pregiudica la originalObject, e viceversa.

O per fare un copia profonda:

var copiedObject = jQuery.extend(true, {}, originalObject)

In ECMAScript 6 c'è Oggetto.assegnare il metodo, che copia i valori di tutti enumerabile propria proprietà da un oggetto all'altro.Per esempio:

var x = {myProp: "value"};
var y = Object.assign({}, x); 

Ma essere consapevoli del fatto che gli oggetti nidificati sono ancora copiato come riferimento.

Per MDN:

  • Se vuoi una copia superficiale, utilizzare Object.assign({}, a)
  • Per "profondo" copiare, utilizzare JSON.parse(JSON.stringify(a))

Non c'è bisogno di librerie esterne, ma è necessario controllare la compatibilità con i browser prima.

Ci sono molte risposte, ma nessuno che cita Oggetto.creare da ECMAScript 5, che certamente non darà una copia esatta, ma imposta la sorgente come il prototipo del nuovo oggetto.

Così, questa non è una risposta esatta alla domanda, ma è una soluzione e così elegante.E funziona meglio per 2 casi:

  1. Dove tale eredità è utile (duh!)
  2. Dove è la fonte oggetto non può essere modificato, rendendo così il rapporto tra i 2 oggetti un non problema.

Esempio:

var foo = { a : 1 };
var bar = Object.create(foo);
foo.a; // 1
bar.a; // 1
foo.a = 2;
bar.a; // 2 - prototype changed
bar.a = 3;
foo.a; // Still 2, since setting bar.a makes it an "own" property

Perché ritengo che questa soluzione sia superiore?È nativo, quindi nessun ciclo, senza ricorsione.Tuttavia, i browser più vecchi avranno bisogno di un polyfill.

Un modo elegante per clonare un oggetto Javascript in una sola riga di codice

Un Object.assign il metodo è parte di ECMAScript 2015 (ES6) standard e fa esattamente quello di cui hai bisogno.

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

L'Oggetto.assegnare() è il metodo utilizzato per copiare i valori di tutti enumerabile proprietà di uno o più oggetti di origine di un oggetto di destinazione.

Per saperne di più...

Il polyfill per supportare i browser più vecchi:

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

Ci sono diversi problemi con più soluzioni su internet.Così ho deciso di fare un follow-up, che comprende, perché il accettato la risposta non dovrebbe essere accettato.

situazione di partenza

Voglio deep-copia un Javascript Object con tutti i suoi figli e i loro figli e così via.Ma siccome io non sono una sorta di sviluppatore normale, il mio Object ha normale properties, circular structures e anche nested objects.

Creiamo quindi un circular structure e un nested object per prima.

function Circ() {
    this.me = this;
}

function Nested(y) {
    this.y = y;
}

Portiamo il tutto in un Object nome a.

var a = {
    x: 'a',
    circ: new Circ(),
    nested: new Nested('a')
};

Successivamente, si desidera copiare a in una variabile di nome b e mutarlo.

var b = a;

b.x = 'b';
b.nested.y = 'b';

Sapete cosa è successo qui, perché se non si sarebbe nemmeno atterrare su questa grande questione.

console.log(a, b);

a --> Object {
    x: "b",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

b --> Object {
    x: "b",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

Ora proviamo a trovare una soluzione.

JSON

Il primo tentativo che ho provato è stato utilizzando JSON.

var b = JSON.parse( JSON.stringify( a ) );

b.x = 'b';
b.nested.y = 'b';

Non sprecare troppo tempo su di esso, si otterrà TypeError: Converting circular structure to JSON.

Copia ricorsiva (accettato "risposta")

Diamo un'occhiata al accettati risposta.

function cloneSO(obj) {
    // Handle the 3 simple types, and null or undefined
    if (null == obj || "object" != typeof obj) 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, len = obj.length; i < len; i++) {
            copy[i] = cloneSO(obj[i]);
        }
        return copy;
    }

    // Handle Object
    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! Its type isn't supported.");
}

Sembra buono, eh?Si tratta di un copia ricorsiva dell'oggetto e maniglie di altri tipi, come Date, ma quello non era un requisito.

var b = cloneSO(a);

b.x = 'b';
b.nested.y = 'b';

Ricorsione e circular structures non funziona bene insieme... RangeError: Maximum call stack size exceeded

soluzione nativa

Dopo aver litigato con il mio collega di lavoro, il mio capo ci ha chiesto cosa è successo, e ha trovato un semplice soluzione dopo alcune ricerche con google.Si chiama Object.create.

var b = Object.create(a);

b.x = 'b';
b.nested.y = 'b';

Questa soluzione è stato aggiunto al Javascript po ' di tempo fa e anche le maniglie circular structure.

console.log(a, b);

a --> Object {
    x: "a",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

b --> Object {
    x: "b",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

...e vedete, non ha funzionato con la struttura nidificata all'interno.

polyfill per la soluzione nativa

C'è un polyfill per Object.create nei vecchi browser come IE 8.È qualcosa come raccomandato da Mozilla, e, naturalmente, non è perfetto e si ottiene lo stesso problema soluzione nativa.

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

var b = clonePF(a);

b.x = 'b';
b.nested.y = 'b';

Ho messo F al di fuori del campo di applicazione in modo che possiamo avere uno sguardo a ciò che instanceof ci dice.

console.log(a, b);

a --> Object {
    x: "a",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

b --> F {
    x: "b",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

console.log(typeof a, typeof b);

a --> object
b --> object

console.log(a instanceof Object, b instanceof Object);

a --> true
b --> true

console.log(a instanceof F, b instanceof F);

a --> false
b --> true

Stesso problema soluzione nativa, ma un po ' peggio di uscita.

il migliore (ma non perfetta) soluzione

Quando scavando in giro, ho trovato una domanda simile (In Javascript, quando si esegue una copia profonda, come faccio a evitare un ciclo, a causa di una proprietà di essere "questo"?) per questo, ma con una migliore soluzione.

function cloneDR(o) {
    const gdcc = "__getDeepCircularCopy__";
    if (o !== Object(o)) {
        return o; // primitive value
    }

    var set = gdcc in o,
        cache = o[gdcc],
        result;
    if (set && typeof cache == "function") {
        return cache();
    }
    // else
    o[gdcc] = function() { return result; }; // overwrite
    if (o instanceof Array) {
        result = [];
        for (var i=0; i<o.length; i++) {
            result[i] = cloneDR(o[i]);
        }
    } else {
        result = {};
        for (var prop in o)
            if (prop != gdcc)
                result[prop] = cloneDR(o[prop]);
            else if (set)
                result[prop] = cloneDR(cache);
    }
    if (set) {
        o[gdcc] = cache; // reset
    } else {
        delete o[gdcc]; // unset again
    }
    return result;
}

var b = cloneDR(a);

b.x = 'b';
b.nested.y = 'b';

E diamo un'occhiata all'output...

console.log(a, b);

a --> Object {
    x: "a",
    circ: Object {
        me: Object { ... }
    },
    nested: Object {
        y: "a"
    }
}

b --> Object {
    x: "b",
    circ: Object {
        me: Object { ... }
    },
    nested: Object {
        y: "b"
    }
}

console.log(typeof a, typeof b);

a --> object
b --> object

console.log(a instanceof Object, b instanceof Object);

a --> true
b --> true

console.log(a instanceof F, b instanceof F);

a --> false
b --> false

I requisiti sono abbinati, ma ci sono ancora alcune piccole questioni, compresa la modifica della instance di nested e circ per Object.

La struttura di alberi che condividono una foglia di non essere copiati, diventeranno due indipendenti foglie:

        [Object]                     [Object]
         /    \                       /    \
        /      \                     /      \
      |/_      _\|                 |/_      _\|  
  [Object]    [Object]   ===>  [Object]    [Object]
       \        /                 |           |
        \      /                  |           |
        _\|  |/_                 \|/         \|/
        [Object]               [Object]    [Object]

conclusione

L'ultima soluzione che utilizza la ricorsione e una cache, potrebbe non essere il migliore, ma è un reale deep-copia dell'oggetto.Gestisce semplice properties, circular structures e nested object, ma esso sarà mess up l'istanza di loro durante la clonazione.

jsfiddle

Se stai bene con una copia superficiale, il underscore.js la biblioteca ha un clone metodo.

y = _.clone(x);

o si può estendere come

copiedObject = _.extend({},originalObject);

OK, immaginate di avere questo oggetto qui di seguito e si desidera clonare:

let obj = {a:1, b:2, c:3}; //ES6

o

var obj = {a:1, b:2, c:3}; //ES5

la risposta è principalmente depeneds su cui ECMAscript si utilizza, in ES6+, si può semplicemente utilizzare Object.assign per fare il clone:

let cloned = Object.assign({}, obj); //new {a:1, b:2, c:3};

o mediante diffusione operatore di simile a questo:

let cloned = {...obj}; //new {a:1, b:2, c:3};

Ma se si utilizza ES5, è possibile utilizzare alcuni metodi, ma il JSON.stringify, basta assicurarsi che non si utilizza per un grande pezzo di dati da copiare, ma potrebbe essere una riga in modo pratico, in molti casi, qualcosa di simile a questo:

let cloned = JSON.parse(JSON.stringify(obj)); 
//new {a:1, b:2, c:3};, can be handy, but avoid using on big chunk of data over and over

Una particolare soluzione poco elegante per l'uso di codifica JSON per rendere profondo copie di oggetti che non hanno metodi di membro.La metodologia è JSON codificare l'oggetto di destinazione, quindi dalla decodifica di esso, si ottiene la copia che si sta cercando.È possibile decodificare come molte volte come si desidera fare tante copie di cui hai bisogno.

Naturalmente, le funzioni non appartengono JSON, quindi questo funziona solo per gli oggetti senza metodi di membro.

Questa metodologia è stata perfetta per il mio caso d'uso, dal momento che sto mantenendo JSON blob in una chiave-valore, e quando sono esposti come oggetti in JavaScript API, ogni oggetto contiene in realtà una copia dello stato originale dell'oggetto in modo che possiamo calcolare il delta dopo che il chiamante ha mutato l'oggetto esposto.

var object1 = {key:"value"};
var object2 = object1;

object2 = JSON.stringify(object1);
object2 = JSON.parse(object2);

object2.key = "a change";
console.log(object1);// returns value

Si può semplicemente utilizzare un la diffusione di proprietà per copiare un oggetto senza riferimenti.Ma attenzione (vedi commenti), la "copia" è solo al livello più basso dell'oggetto/livello di array.Proprietà nidificate sono ancora riferimenti!


Clone completo:

let x = {a: 'value1'}
let x2 = {...x}

// => mutate without references:

x2.a = 'value2'
console.log(x.a)    // => 'value1'

Clone con riferimenti al secondo livello:

const y = {a: {b: 'value3'}}
const y2 = {...y}

// => nested object is still a references:

y2.a.b = 'value4'
console.log(y.a.b)    // => 'value4'

JavaScript in realtà non supporta deep cloni in modo nativo.Utilizzare una funzione di utilità.Per esempio Ramda:

http://ramdajs.com/docs/#clone

Per coloro che utilizzano AngularJS, c'è anche un metodo diretto per la clonazione o l'estensione degli oggetti in questa libreria.

var destination = angular.copy(source);

o

angular.copy(source, destination);

Più angolare.copia documentazione...

A. Levy risposta è quasi completa, ecco il mio piccolo contributo: non c'è un modo per gestire ricorsiva riferimenti, vedere questa linea

if(this[attr]==this) copy[attr] = copy;

Se l'oggetto è XML DOM elemento, dobbiamo usare cloneNode invece

if(this.cloneNode) return this.cloneNode(true);

Ispirato da A. Levy esauriente di studio e di Calvino prototipazione approccio, offro questa soluzione:

Object.prototype.clone = function() {
  if(this.cloneNode) return this.cloneNode(true);
  var copy = this instanceof Array ? [] : {};
  for(var attr in this) {
    if(typeof this[attr] == "function" || this[attr]==null || !this[attr].clone)
      copy[attr] = this[attr];
    else if(this[attr]==this) copy[attr] = copy;
    else copy[attr] = this[attr].clone();
  }
  return copy;
}

Date.prototype.clone = function() {
  var copy = new Date();
  copy.setTime(this.getTime());
  return copy;
}

Number.prototype.clone = 
Boolean.prototype.clone =
String.prototype.clone = function() {
  return this;
}

Vedere anche Andy Burke nota nelle risposte.

Ecco una funzione che è possibile utilizzare.

function clone(obj) {
    if(obj == null || typeof(obj) != 'object')
        return obj;    
    var temp = new obj.constructor(); 
    for(var key in obj)
        temp[key] = clone(obj[key]);    
    return temp;
}

Da questo articolo: Come la copia di array e oggetti in Javascript da Brian Huisman:

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

In ES-6 si può semplicemente utilizzare l'Oggetto.assegnare(...).Ex:

let obj = {person: 'Thor Odinson'};
let clone = Object.assign({}, obj);

Un buon riferimento è qui:https://googlechrome.github.io/samples/object-assign-es6/

In ECMAScript 2018

let objClone = { ...obj };

Essere consapevoli del fatto che oggetti nidificati sono ancora copiato come riferimento.

È possibile clonare un oggetto e rimuovere qualsiasi riferimento alla precedente utilizzando una singola riga di codice.Semplicemente fare:

var obj1 = { text: 'moo1' };
var obj2 = Object.create(obj1); // Creates a new clone without references

obj2.text = 'moo2'; // Only updates obj2's text property

console.log(obj1, obj2); // Outputs: obj1: {text:'moo1'}, obj2: {text:'moo2'}

Per i browser / motori che attualmente non Oggetto di supporto.creare è possibile utilizzare questo polyfill:

// Polyfill Object.create if it does not exist
if (!Object.create) {
    Object.create = function (o) {
        var F = function () {};
        F.prototype = o;
        return new F();
    };
}

Risposta nuova per una vecchia questione!Se avete il piacere di avere utilizzando ECMAScript 2016 (ES6) con La Diffusione Di Sintassi, è facile.

keepMeTheSame = {first: "Me!", second: "You!"};
cloned = {...keepMeTheSame}

Questo fornisce un metodo per pulire una copia superficiale di un oggetto.Fare una copia profonda, significato makign una nuova copia di ogni valore in ogni modo ricorsivo oggetto nidificato, richiede più pesanti in soluzioni di cui sopra.

JavaScript è in continua evoluzione.

let clone = Object.assign( Object.create( Object.getPrototypeOf(obj)), obj)

ES6 soluzione se si vuole (di poco) un clone istanza della classe e non solo un oggetto di proprietà.

Interessati clonazione di oggetti semplici:

JSON.parse(JSON.stringify(json_original));

Fonte : Come copiare un oggetto JavaScript per nuova variabile NON da riferimento?

Utilizzando Lodash:

var y = _.clone(x, true);

Penso che ci sia una semplice e la risposta di lavoro.In una copia profonda ci sono due problemi:

  1. Mantenere le proprietà indipendenti.
  2. E mantenere i metodi vivo in oggetto clonato.

Quindi penso che una soluzione semplice sarà per prima la serializzazione e la deserializzazione e poi fare un'assegnare per le funzioni di copia troppo.

let deepCloned = JSON.parse(JSON.stringify(source));
let merged = Object.assign({}, source);
Object.assign(merged, deepCloned);

Anche se questa domanda ha molte risposte, spero che questo aiuta troppo.

Per una copia profonda e clone, JSON.stringify poi JSON.analizzare l'oggetto:

obj = { a: 0 , b: { c: 0}};
let deepClone = JSON.parse(JSON.stringify(obj));
obj.a = 5;
obj.b.c = 5;
console.log(JSON.stringify(deepClone)); // { a: 0, b: { c: 0}}

Volevo solo aggiungere a tutti i Object.create le soluzioni in questo post, che questo non funziona nel modo desiderato con nodejs.

In Firefox il risultato di

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

è

{test:"test"}.

In nodejs è

{}

Questo è un adattamento di A.Levy codice per gestire anche la clonazione di funzioni e di più/riferimenti ciclici - che cosa questo significa è che se due proprietà in l'albero che viene clonato sono riferimenti allo stesso oggetto, l'oggetto clonato albero avrà queste proprietà punto per uno stesso clone dell'oggetto di riferimento.Questo risolve anche il caso di ciclico dipendenze che, se non gestita, conduce a un loop infinito.La complessità dell'algoritmo è 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;
}

Alcuni test rapido

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

Ho scritto la mia realizzazione.Non so se conta come una soluzione migliore:

/*
    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)    /
*/

Segue l'attuazione:

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

Jan Turoň la risposta di cui sopra è molto vicino, e potrebbe essere meglio usare un browser a causa di problemi di compatibilità, ma è potenzialmente causa di alcuni strani problemi di enumerazione.Per esempio, l'esecuzione di:

for ( var i in someArray ) { ... }

Assegna il metodo clone() per i dopo l'iterazione attraverso gli elementi dell'array.Ecco un adattamento che evita l'enumerazione e funziona con node.js:

Object.defineProperty( Object.prototype, "clone", {
    value: function() {
        if ( this.cloneNode )
        {
            return this.cloneNode( true );
        }

        var copy = this instanceof Array ? [] : {};
        for( var attr in this )
        {
            if ( typeof this[ attr ] == "function" || this[ attr ] == null || !this[ attr ].clone )
            {
                copy[ attr ] = this[ attr ];
            }
            else if ( this[ attr ] == this )
            {
                copy[ attr ] = copy;
            }
            else
            {
                copy[ attr ] = this[ attr ].clone();
            }
        }
        return copy;
    }
});

Object.defineProperty( Date.prototype, "clone", {
    value: function() {
        var copy = new Date();
        copy.setTime( this.getTime() );
        return copy;
    }
});

Object.defineProperty( Number.prototype, "clone", { value: function() { return this; } } );
Object.defineProperty( Boolean.prototype, "clone", { value: function() { return this; } } );
Object.defineProperty( String.prototype, "clone", { value: function() { return this; } } );

Questo evita di fare il metodo clone() enumerabile perché defineProperty() default enumerabile false.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top