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 .cloneNode
existe por esse motivo.
Como eu quero principalmente usar clones profundos no node.js
uso 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 o
sem escrever um analisador JS em JS.
Não deve haver mais necessidade no mundo real de tal função. Isso é mero interesse acadêmico.
fonte
clone
função genérica . O objeto em questão deve expor seu próprioclone
método feito sob medida .Respostas:
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.
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
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 in
com 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 porif( 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
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 in
soluçã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
cloneNode
está fazendo pelo DOM.Você decide.
fonte
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.false && item.constructor
? Isso não éif
inútil?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 aelse
cláusula toda vez que a condição for avaliada e, ainda assim, há uma razão para o código estar lá.Maneira muito simples, talvez muito simples:
fonte
cloneDeep
no Lodash.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).
fonte
var o = { a:1, b:2 } ; o["oo"] = { c:3, m:o };
bObject[k] = (v === null) ? null : (typeof v === "object") ? copy(v) : v;
Aqui está uma função ES6 que também funcionará para objetos com referências cíclicas:
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
has
desejá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.
fonte
if (object instanceof Set) Array.from(object, val => result.add(deepClone(val, hash)));
A biblioteca da biblioteca contrib Underscore.js tem uma função chamada instantâneo que clona profundamente um objeto
trecho da fonte:
uma vez que a biblioteca está ligada ao seu projeto, invoque a função simplesmente usando
fonte
Este é o método de clonagem profunda que uso, acho ótimo, espero que você dê sugestões
fonte
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: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):
A solução mais simples e razoável de clone profundo, assumindo um objeto raiz não nulo e sem seleção de membro:
fonte
Lo-Dash , agora um superconjunto de Underscore.js , tem algumas funções de clone profundas:
_.cloneDeep(object)
_.cloneDeepWith(object, (val) => {if(_.isElement(val)) return val.cloneNode(true)})
o segundo parâmetro é uma função que é chamada para produzir o valor clonado.
De uma resposta do próprio autor :
fonte
baseClone
do lodash pode fornecer algumas idéias.A função abaixo é a maneira mais eficiente de clonar profundamente objetos javascript.
fonte
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.
fonte
Percebi que o Map deve requerer um tratamento especial, portanto, com todas as sugestões neste thread, o código será:
fonte
minha adição a todas as respostas
fonte
Isso funciona para matrizes, objetos e primitivos. Algoritmo duplamente recursivo que alterna entre dois métodos de passagem:
fonte
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.
fonte
Use immutableJS
Ou lodash / mesclar
fonte
Este, usando referência circular, funciona para mim
fonte
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.
fonte
Esta solução evitará problemas de recursão ao usar [... target] ou {... target}
fonte