Como clonar profundamente em javascript

105

Como você clona profundamente um objeto Javascript?

Eu sei que existem várias funções baseadas em frameworks como JSON.parse(JSON.stringify(o))e, $.extend(true, {}, o)mas não quero usar um framework como esse.

Qual é a maneira mais elegante ou eficiente de criar um clone profundo.

Nós nos preocupamos com casos extremos, como a clonagem de matrizes. Não quebrando cadeias de protótipos, lidando com a auto-referência.

Não nos importamos em oferecer suporte à cópia de objetos DOM porque .cloneNodeexiste por esse motivo.

Como eu quero principalmente usar clones profundos no node.jsuso de recursos ES5 do motor V8, é aceitável.

[Editar]

Antes que alguém sugira, deixe-me mencionar que há uma diferença distinta entre criar uma cópia herdando prototipicamente do objeto e clonando -o. O primeiro bagunça a cadeia de protótipos.

[Editar mais]

Depois de ler sua resposta, descobri que clonar objetos inteiros é um jogo muito perigoso e difícil. Tome por exemplo o seguinte objeto baseado em fechamento

var o = (function() {
     var magic = 42;

     var magicContainer = function() {
          this.get = function() { return magic; };
          this.set = function(i) { magic = i; };
     }

      return new magicContainer;
}());

var n = clone(o); // how to implement clone to support closures

Existe alguma maneira de escrever uma função de clone que clona o objeto, tem o mesmo estado no momento da clonagem, mas não pode alterar o estado osem escrever um analisador JS em JS.

Não deve haver mais necessidade no mundo real de tal função. Isso é mero interesse acadêmico.

Raynos
fonte
2
Antes de ser marcado como duplicado, olhei em stackoverflow.com/questions/122102/… e não encontrei nenhuma resposta que lidasse com todos os casos extremos.
Raynos
O requisito na seção "edição adicional" é impossível de alcançar sem a "ajuda" do próprio objeto, uma vez que tais variáveis ​​privadas são verdadeiramente privadas e, por consequência, não acessíveis por uma clonefunção genérica . O objeto em questão deve expor seu próprio clonemétodo feito sob medida .
trincot de
Estive lendo muito sobre isso esta noite e entre os recursos que encontrei estava esta postagem de blog de aparência horrível que inclui alguns hacks para acessar o algoritmo de clone estruturado no navegador: dassur.ma/things/deep-copy
Cat

Respostas:

67

Realmente depende do que você gostaria de clonar. Este é um objeto JSON verdadeiro ou apenas qualquer objeto em JavaScript? Se você quiser fazer qualquer clone, poderá ter alguns problemas. Qual problema? Explicarei abaixo, mas primeiro, um exemplo de código que clona literais de objeto, quaisquer primitivos, matrizes e nós DOM.

function clone(item) {
    if (!item) { return item; } // null, undefined values check

    var types = [ Number, String, Boolean ], 
        result;

    // normalizing primitives if someone did new String('aaa'), or new Number('444');
    types.forEach(function(type) {
        if (item instanceof type) {
            result = type( item );
        }
    });

    if (typeof result == "undefined") {
        if (Object.prototype.toString.call( item ) === "[object Array]") {
            result = [];
            item.forEach(function(child, index, array) { 
                result[index] = clone( child );
            });
        } else if (typeof item == "object") {
            // testing that this is DOM
            if (item.nodeType && typeof item.cloneNode == "function") {
                result = item.cloneNode( true );    
            } else if (!item.prototype) { // check that this is a literal
                if (item instanceof Date) {
                    result = new Date(item);
                } else {
                    // it is an object literal
                    result = {};
                    for (var i in item) {
                        result[i] = clone( item[i] );
                    }
                }
            } else {
                // depending what you would like here,
                // just keep the reference, or create new object
                if (false && item.constructor) {
                    // would not advice to do that, reason? Read below
                    result = new item.constructor();
                } else {
                    result = item;
                }
            }
        } else {
            result = item;
        }
    }

    return result;
}

var copy = clone({
    one : {
        'one-one' : new String("hello"),
        'one-two' : [
            "one", "two", true, "four"
        ]
    },
    two : document.createElement("div"),
    three : [
        {
            name : "three-one",
            number : new Number("100"),
            obj : new function() {
                this.name = "Object test";
            }   
        }
    ]
})

E agora, vamos falar sobre os problemas que você pode ter ao começar a clonar objetos REAIS. Estou falando agora, sobre objetos que você cria fazendo algo como

var User = function(){}
var newuser = new User();

Claro que você pode cloná-los, não é um problema, todo objeto expõe a propriedade do construtor, e você pode usá-lo para clonar objetos, mas nem sempre funcionará. Você também pode fazer coisas simples for incom esses objetos, mas vai na mesma direção - problemas. Também incluí a funcionalidade de clone dentro do código, mas ela foi excluída por if( false )declaração.

Então, por que a clonagem pode ser uma dor? Bem, em primeiro lugar, todo objeto / instância pode ter algum estado. Você nunca pode ter certeza de que seus objetos não têm, por exemplo, uma variável privada, e se for esse o caso, ao clonar o objeto, você apenas quebra o estado.

Imagine que não há estado, tudo bem. Então ainda temos outro problema. A clonagem por meio do método "construtor" nos dará outro obstáculo. É uma dependência de argumentos. Você nunca pode ter certeza de que alguém que criou este objeto não o fez, algum tipo de

new User({
   bike : someBikeInstance
});

Se for esse o caso, você está sem sorte, someBikeInstance provavelmente foi criado em algum contexto e esse contexto é desconhecido para o método de clone.

Então o que fazer? Você ainda pode fazer a for insolução e tratar tais objetos como literais de objetos normais, mas talvez seja uma ideia não clonar tais objetos e apenas passar a referência deste objeto?

Outra solução é - você pode definir uma convenção de que todos os objetos que devem ser clonados devem implementar essa parte por si próprios e fornecer o método de API apropriado (como cloneObject). Algo que cloneNodeestá fazendo pelo DOM.

Você decide.

nemisj
fonte
Cheguei a enfrentar o obstáculo de lidar com objetos que usam fechos para esconder meu estado. Como alguém pode clonar um objeto e seu estado inteiro, mas ainda garantir que o clone não altere o estado original por si mesmo. Um ponto positivo para result = new item.constructor();ser ruim é que, dada a função de construtor e o objeto de item, você deve ser capaz de RE quaisquer parâmetros passados ​​para o construtor.
Raynos
7
@Raynos: se os objetos usam closures para ocultar o estado, você não pode cloná-los. Daí o termo 'fechamento'. Como nemisj diz no final, a melhor maneira é implementar um método de API para clonagem (ou serialização / desserialização) se essa for uma opção.
Michiel Kalkman
@MichielKalkman Tive a sensação de que era esse o caso. Embora alguém pudesse ter uma solução realmente inteligente para isso.
Raynos
Qual é o efeito de false && item.constructor? Isso não é ifinútil?
Gabriel Petrovay
2
@GabrielPetrovay Isso ifé 'inútil' do ponto de vista funcional, porque nunca será executado, mas tem o propósito acadêmico de mostrar uma implementação hipotética que se pode tentar usar, que o autor não aconselha pelo motivo explicado posteriormente. Então, sim, ele irá disparar a elsecláusula toda vez que a condição for avaliada e, ainda assim, há uma razão para o código estar lá.
Gui Imamura
154

Maneira muito simples, talvez muito simples:

var cloned = JSON.parse(JSON.stringify(objectToClone));
G. Ghez
fonte
12
Ótimo, a menos que o valor de um objeto seja uma função, momento em que você terá que usar algo como a resposta aceita. Ou use uma função auxiliar como cloneDeepno Lodash.
Matthoiland
31
Se o valor de um objeto é uma função, o objeto não é JSON.
Jos de Jong
5
Qual caso de uso pode justificar clonar uma função em vez de apenas usá-la?
G. Ghez
3
Se bem me lembro, isso também converte datas em strings.
Peter
3
@ G.Ghez se você clonar um objeto que contém uma função, essa função será perdida.
Peter
38

A JSON.parse(JSON.stringify())combinação para copiar objetos Javascript em profundidade é um hack ineficaz, pois foi feito para dados JSON. Ele não suporta valores de undefinedou function () {}e simplesmente os ignora (ou nullos ignora ) ao "sequenciar" (empacotar) o objeto Javascript em JSON.

Uma solução melhor é usar uma função de cópia profunda. A função abaixo copia objetos em profundidade e não requer uma biblioteca de terceiros (jQuery, LoDash, etc).

function copy(aObject) {
  if (!aObject) {
    return aObject;
  }

  let v;
  let bObject = Array.isArray(aObject) ? [] : {};
  for (const k in aObject) {
    v = aObject[k];
    bObject[k] = (typeof v === "object") ? copy(v) : v;
  }

  return bObject;
}
tfmontague
fonte
7
Exceto quando aObject (ou outro objeto que ele contém) contém uma auto-referência a si mesmo ... stackoverflow ™!
e2-e4
@ ringø - Você pode fornecer alguns casos de teste de "autorreferência"?
tfmontague de
4
var o = { a:1, b:2 } ; o["oo"] = { c:3, m:o };
e2-e4
4
Eu gosto dessa solução. A única solução para mim foi lidar com valores nulos: bObject[k] = (v === null) ? null : (typeof v === "object") ? copy(v) : v;
David Kirkland
2
Esta função é simples, compreensível e abrange quase todos os casos. No mundo do JavaScript, isso é o mais perfeito possível.
icc97 de
21

Aqui está uma função ES6 que também funcionará para objetos com referências cíclicas:

function deepClone(obj, hash = new WeakMap()) {
    if (Object(obj) !== obj) return obj; // primitives
    if (hash.has(obj)) return hash.get(obj); // cyclic reference
    const result = obj instanceof Set ? new Set(obj) // See note about this!
                 : obj instanceof Map ? new Map(Array.from(obj, ([key, val]) => 
                                        [key, deepClone(val, hash)])) 
                 : obj instanceof Date ? new Date(obj)
                 : obj instanceof RegExp ? new RegExp(obj.source, obj.flags)
                 // ... add here any specific treatment for other classes ...
                 // and finally a catch-all:
                 : obj.constructor ? new obj.constructor() 
                 : Object.create(null);
    hash.set(obj, result);
    return Object.assign(result, ...Object.keys(obj).map(
        key => ({ [key]: deepClone(obj[key], hash) }) ));
}

// Sample data
var p = {
  data: 1,
  children: [{
    data: 2,
    parent: null
  }]
};
p.children[0].parent = p;

var q = deepClone(p);

console.log(q.children[0].parent.data); // 1

Uma nota sobre conjuntos e mapas

Como lidar com as chaves de Sets e Maps é discutível: essas chaves são frequentemente primitivas (neste caso não há debate), mas também podem ser objetos. Nesse caso, a questão é: essas chaves devem ser clonadas?

Pode-se argumentar que isso deve ser feito, de modo que, se esses objetos sofrerem mutação na cópia, os objetos no original não serão afetados e vice-versa.

Por outro lado, seria hasdesejável que, se definir / mapear uma chave, isso fosse verdade tanto no original quanto na cópia - pelo menos antes de qualquer alteração ser feita em qualquer um deles. Seria estranho se a cópia fosse um Conjunto / Mapa com chaves que nunca ocorreram antes (pois foram criadas durante o processo de clonagem): com certeza isso não é muito útil para qualquer código que precisa saber se um determinado objeto é um digite esse conjunto / mapa ou não.

Como você notou, eu sou mais da segunda opinião: as chaves de Sets e Maps são valores (talvez referências ) que devem permanecer os mesmos.

Freqüentemente, essas escolhas também aparecerão com outros objetos (talvez personalizados). Não há uma solução geral, pois depende muito de como o objeto clonado deve se comportar em seu caso específico.

trincote
fonte
não aceita data e regexp
mkeremguc
1
@mkeremguc, obrigado pelo seu comentário. Eu atualizei o código para suportar data e regexp.
trincot
Ótima solução, incluindo mapa. Só falta suporte para clonagem de conjuntos ES6.
Robert Biggs,
Se não me engano, você provavelmente pode adicionar suporte para conjuntos com:if (object instanceof Set) Array.from(object, val => result.add(deepClone(val, hash)));
Robert Biggs
@RobertBiggs, essa é uma possibilidade, mas na minha opinião se um Set tem uma certa chave, isso também deveria ser verdade na versão clonada desse Set. Com o seu código sugerido, isso não seria verdadeiro se as chaves fossem objetos. Portanto, eu sugeriria não clonar as chaves - eu realmente acho que então se comportaria mais conforme o esperado. Veja a atualização da minha resposta a esse respeito.
trincot de
10

A biblioteca da biblioteca contrib Underscore.js tem uma função chamada instantâneo que clona profundamente um objeto

trecho da fonte:

snapshot: function(obj) {
  if(obj == null || typeof(obj) != 'object') {
    return obj;
  }

  var temp = new obj.constructor();

  for(var key in obj) {
    if (obj.hasOwnProperty(key)) {
      temp[key] = _.snapshot(obj[key]);
    }
  }

  return temp;
}

uma vez que a biblioteca está ligada ao seu projeto, invoque a função simplesmente usando

_.snapshot(object);
svarog
fonte
4
boa solução, apenas um ponto a lembrar: o clone e o original compartilham o mesmo protótipo. se isso for um problema, é possível adicionar "temp .__ proto__ = .snapshot (obj .__ proto_ );" logo acima da "temperatura de retorno" e para oferecer suporte a classes integradas com propriedades marcadas como 'sem enumerar', você pode iterar em getOwnPropertyNames () em vez de "for (var key in obj)"
Ronen Ness
3

Este é o método de clonagem profunda que uso, acho ótimo, espero que você dê sugestões

function deepClone (obj) {
    var _out = new obj.constructor;

    var getType = function (n) {
        return Object.prototype.toString.call(n).slice(8, -1);
    }

    for (var _key in obj) {
        if (obj.hasOwnProperty(_key)) {
            _out[_key] = getType(obj[_key]) === 'Object' || getType(obj[_key]) === 'Array' ? deepClone(obj[_key]) : obj[_key];
        }
    }
    return _out;
}
Ponto
fonte
3

Como outros notaram sobre esta e outras questões semelhantes, clonar um "objeto", no sentido geral, é duvidoso em JavaScript.

No entanto, há uma classe de objetos, que chamo de objetos de "dados", ou seja, aqueles construídos simplesmente a partir de { ... }literais e / ou atribuições de propriedade simples ou desserializados de JSON para os quais é razoável querer clonar. Só hoje eu queria aumentar artificialmente os dados recebidos de um servidor em 5x para testar o que acontece com um grande conjunto de dados, mas o objeto (uma matriz) e seus filhos tinham que ser objetos distintos para que as coisas funcionassem corretamente. A clonagem me permitiu fazer isso para multiplicar meu conjunto de dados:

return dta.concat(clone(dta),clone(dta),clone(dta),clone(dta));

O outro lugar em que frequentemente acabo clonando objetos de dados é para enviar dados de volta ao host, onde desejo remover os campos de estado do objeto no modelo de dados antes de enviá-lo. Por exemplo, posso remover todos os campos que começam com "_" do objeto à medida que ele é clonado.

Este é o código que acabei escrevendo para fazer isso genericamente, incluindo matrizes de suporte e um seletor para escolher quais membros clonar (que usa uma string de "caminho" para determinar o contexto):

function clone(obj,sel) {
    return (obj ? _clone("",obj,sel) : obj);
    }

function _clone(pth,src,sel) {
    var ret=(src instanceof Array ? [] : {});

    for(var key in src) {
        if(!src.hasOwnProperty(key)) { continue; }

        var val=src[key], sub;

        if(sel) {
            sub+=pth+"/"+key;
            if(!sel(sub,key,val)) { continue; }
            }

        if(val && typeof(val)=='object') {
            if     (val instanceof Boolean) { val=Boolean(val);        }
            else if(val instanceof Number ) { val=Number (val);        }
            else if(val instanceof String ) { val=String (val);        }
            else                            { val=_clone(sub,val,sel); }
            }
        ret[key]=val;
        }
    return ret;
    }

A solução mais simples e razoável de clone profundo, assumindo um objeto raiz não nulo e sem seleção de membro:

function clone(src) {
    var ret=(src instanceof Array ? [] : {});
    for(var key in src) {
        if(!src.hasOwnProperty(key)) { continue; }
        var val=src[key];
        if(val && typeof(val)=='object') { val=clone(val);  }
        ret[key]=val;
        }
    return ret;
    }
Lawrence Dol
fonte
2

Lo-Dash , agora um superconjunto de Underscore.js , tem algumas funções de clone profundas:

De uma resposta do próprio autor :

lodash underscore build é fornecido para garantir a compatibilidade com a última versão estável do Underscore.

CPHPython
fonte
pergunta disse "eu não quero usar bibliotecas"
Femi Oni
@FemiOni a questão não tem nada sobre bibliotecas (mesmo em suas edições antigas) ... Algumas outras respostas aqui também estão usando uma biblioteca ou outra também.
CPHPython
Resposta @FemiOni votada ontem. Eu me pergunto ... De qualquer forma, este é um local de aprendizado e apenas no caso de alguém realmente implementar o clone profundo em si, o código-fontebaseClone do lodash pode fornecer algumas idéias.
CPHPython
@FemiOni o objeto JSON não é uma biblioteca nem um framework ... Se você vai implementar a função, sugiro que você procure em uma das bibliotecas de código aberto e use as partes de que você precisa (muitas foram testadas por anos). Isso evitará bugs e considerações perdidas a longo prazo.
CPHPython
2

A função abaixo é a maneira mais eficiente de clonar profundamente objetos javascript.

function deepCopy(obj){
    if (!obj || typeof obj !== "object") return obj;

    var retObj = {};

    for (var attr in obj){
        var type = obj[attr];

        switch(true){
            case (type instanceof Date):
                var _d = new Date();
                _d.setDate(type.getDate())
                retObj[attr]= _d;
                break;

            case (type instanceof Function):
                retObj[attr]= obj[attr];
                break;

            case (type instanceof Array):
                var _a =[];
                for (var e of type){
                    //_a.push(e);
                    _a.push(deepCopy(e));
                }
                retObj[attr]= _a;
                break;

            case (type instanceof Object):
                var _o ={};
                for (var e in type){
                    //_o[e] = type[e];
                    _o[e] = deepCopy(type[e]);
                }
                retObj[attr]= _o;
                break;

            default:
                retObj[attr]= obj[attr];
        }
    }
    return retObj;
}

var obj = {
    string: 'test',
    array: ['1'],
    date: new Date(),
    object:{c: 2, d:{e: 3}},
    function: function(){
        return this.date;
    }
};

var copyObj = deepCopy(obj);

console.log('object comparison', copyObj === obj); //false
console.log('string check', copyObj.string === obj.string); //true
console.log('array check', copyObj.array === obj.array); //false
console.log('date check', copyObj2.date === obj.date); //false
console.log('object check', copyObj.object === obj.object); //false
console.log('function check', copyObj.function() === obj.function()); //true
Kooldandy
fonte
2
Você tem algum argumento para apoiar sua afirmação?
Fabian von Ellerts de
Eu dei um exemplo abaixo da função. Você tem alguma dúvida?
Kooldandy de
Esses exemplos mostram que a função está funcionando, o que é legal, mas por que é "a maneira mais eficiente"?
Fabian von Ellerts de
Porque está copiando recursivamente os atributos do objeto em um único loop. Além disso, data, função, objeto, matriz, número, seqüência de caracteres, todos esses são tratados corretamente. Você tem alguma outra maneira de contornar?
Kooldandy de
2

Não deve haver mais necessidade no mundo real de tal função. Isso é mero interesse acadêmico.

Como puramente um exercício, esta é uma maneira mais funcional de fazê-lo. É uma extensão da resposta de @tfmontague, pois eu sugeri adicionar um bloco de proteção lá. Mas visto que me sinto compelido a ES6 e funcionalizar todas as coisas, aqui está minha versão pimped. Isso complica a lógica, pois você precisa mapear sobre a matriz e reduzir sobre o objeto, mas evita quaisquer mutações.

const cloner = (x) => {
    const recurseObj = x => (typeof x === 'object') ? cloner(x) : x
    const cloneObj = (y, k) => {
        y[k] = recurseObj(x[k])
        return y
    }
    // Guard blocks
    // Add extra for Date / RegExp if you want
    if (!x) {
        return x
    }
    if (Array.isArray(x)) {
        return x.map(recurseObj)
    }
    return Object.keys(x).reduce(cloneObj, {})
}
const tests = [
    null,
    [],
    {},
    [1,2,3],
    [1,2,3, null],
    [1,2,3, null, {}],
    [new Date('2001-01-01')], // FAIL doesn't work with Date
    {x:'', y: {yx: 'zz', yy: null}, z: [1,2,3,null]},
    {
        obj : new function() {
            this.name = "Object test";
        }
    } // FAIL doesn't handle functions
]
tests.map((x,i) => console.log(i, cloner(x)))

icc97
fonte
1

Percebi que o Map deve requerer um tratamento especial, portanto, com todas as sugestões neste thread, o código será:

function deepClone( obj ) {
    if( !obj || true == obj ) //this also handles boolean as true and false
        return obj;
    var objType = typeof( obj );
    if( "number" == objType || "string" == objType ) // add your immutables here
        return obj;
    var result = Array.isArray( obj ) ? [] : !obj.constructor ? {} : new obj.constructor();
    if( obj instanceof Map )
        for( var key of obj.keys() )
            result.set( key, deepClone( obj.get( key ) ) );
    for( var key in obj )
        if( obj.hasOwnProperty( key ) )
            result[key] = deepClone( obj[ key ] );
    return result;
}
Dmitriy Pichugin
fonte
1

minha adição a todas as respostas

deepCopy = arr => {
  if (typeof arr !== 'object') return arr
  if(arr.pop) return [...arr].map(deepCopy)
  const copy = {}
  for (let prop in arr)
    copy[prop] = deepCopy(arr[prop])
  return copy
}
Ronald C
fonte
0

Isso funciona para matrizes, objetos e primitivos. Algoritmo duplamente recursivo que alterna entre dois métodos de passagem:

const deepClone = (objOrArray) => {

  const copyArray = (arr) => {
    let arrayResult = [];
    arr.forEach(el => {
        arrayResult.push(cloneObjOrArray(el));
    });
    return arrayResult;
  }

  const copyObj = (obj) => {
    let objResult = {};
    for (key in obj) {
      if (obj.hasOwnProperty(key)) {
        objResult[key] = cloneObjOrArray(obj[key]);
      }
    }
    return objResult;
  }

  const cloneObjOrArray = (el) => {
    if (Array.isArray(el)) {
      return copyArray(el);
    } else if (typeof el === 'object') {
      return copyObj(el);
    } else {
      return el;
    }
  }

  return cloneObjOrArray(objOrArray);
}
Eric Grotke
fonte
0

Podemos utilizar recursão para fazer deepCopy. Ele pode criar uma cópia de array, objeto, array de objeto, objeto com função. se quiser, você pode adicionar funções para outro tipo de estrutura de dados, como mapa, etc.

function deepClone(obj) {
         var retObj;
        _assignProps = function(obj, keyIndex, retObj) {
               var subType = Object.prototype.toString.call(obj[keyIndex]);
               if(subType === "[object Object]" || subType === "[object Array]") {
                    retObj[keyIndex] = deepClone(obj[keyIndex]);
               }
               else {
                     retObj[keyIndex] = obj[keyIndex];
               }
        };

        if(Object.prototype.toString.call(obj) === "[object Object]") {
           retObj = {};
           for(key in obj) {
               this._assignProps(obj, key, retObj);
           }
        }
        else if(Object.prototype.toString.call(obj) == "[object Array]") {
           retObj = [];
           for(var i = 0; i< obj.length; i++) {
              this._assignProps(obj, i, retObj);
            }
        };

        return retObj;
    };
Ninguém
fonte
0

Use immutableJS

import { fromJS } from 'immutable';

// An object we want to clone
let objA = { 
   a: { deep: 'value1', moreDeep: {key: 'value2'} } 
};

let immB = fromJS(objA); // Create immutable Map
let objB = immB.toJS(); // Convert to plain JS object

console.log(objA); // Object { a: { deep: 'value1', moreDeep: {key: 'value2'} } }
console.log(objB); // Object { a: { deep: 'value1', moreDeep: {key: 'value2'} } }

// objA and objB are equalent, but now they and their inner objects are undependent
console.log(objA === objB); // false
console.log(objA.a === objB.a); // false
console.log(objA.moreDeep === objB.moreDeep); // false

Ou lodash / mesclar

import merge from 'lodash/merge'

var objA = {
    a: [{ 'b': 2 }, { 'd': 4 }]
};
// New deeply cloned object:
merge({}, objA ); 

// We can also create new object from several objects by deep merge:
var objB = {
    a: [{ 'c': 3 }, { 'e': 5 }]
};
merge({}, objA , objB ); // Object { a: [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] }
Alexandre Ladonin
fonte
0

Este, usando referência circular, funciona para mim

 //a test-object with circular reference :
 var n1 = {   id:0,   text:"aaaaa",   parent:undefined} 
 var n2 = {  id:1,   text:"zzzzz",   parent:undefined } 
 var o = { arr:[n1,n2],   parent:undefined } 
 n1.parent = n2.parent = o;
 var obj = {   a:1,   b:2,   o:o }
 o.parent = obj;

 function deepClone(o,output){ 

     if(!output) output = {};  
     if(o.______clone) return o.______clone;
     o.______clone = output.______clone = output;

   for(var z in o){

     var obj = o[z];
     if(typeof(obj) == "object") output[z] = deepClone(obj)
     else output[z] = obj; 
    }

   return output;
}

console.log(deepClone(obj));
Tom Lecoz
fonte
0

var newDate = new Date (this.oldDate); Eu estava passando oldDate para funcionar e gerando newDate a partir de this.oldDate, mas estava mudando this.oldDate também. Usei essa solução e funcionou.

Tejas Pawar
fonte
0

Esta solução evitará problemas de recursão ao usar [... target] ou {... target}

function shallowClone(target) {
  if (typeof a == 'array') return [...target]
  if (typeof a == 'object') return {...target}
  return target
}

/* set skipRecursion to avoid throwing an exception on recursive references */
/* no need to specify refs, or path -- they are used interally */
function deepClone(target, skipRecursion, refs, path) {
  if (!refs) refs = []
  if (!path) path = ''
  if (refs.indexOf(target) > -1) {
    if (skipRecursion) return null
    throw('Recursive reference at ' + path)
  }
  refs.push(target)
  let clone = shallowCopy(target)
  for (i in target) target[i] = deepClone(target, refs, path + '.' + i)
  return clone
}
pbatey
fonte