Eu tenho um objeto x
. Gostaria de copiá-lo como objeto y
, de modo que as alterações y
não sejam modificadas x
. Percebi que copiar objetos derivados de objetos JavaScript internos resultará em propriedades extras e indesejadas. Isso não é um problema, pois estou copiando um dos meus próprios objetos construídos literalmente.
Como clonar corretamente um objeto JavaScript?
javascript
clone
javascript-objects
soundly_typed
fonte
fonte
mObj=JSON.parse(JSON.stringify(jsonObject));
Object.create(o)
, ele faz tudo o que o autor pede?var x = { deep: { key: 1 } }; var y = Object.create(x); x.deep.key = 2;
Após fazer isso,y.deep.key
também será 2, daí Object.create não pode ser usado para clonagem ...Respostas:
Fazer isso para qualquer objeto em JavaScript não será simples ou direto. Você terá o problema de escolher erroneamente atributos do protótipo do objeto que devem ser deixados no protótipo e não copiados para a nova instância. Se, por exemplo, você estiver adicionando um
clone
métodoObject.prototype
, como algumas respostas mostram, será necessário pular explicitamente esse atributo. Mas e se houver outros métodos adicionais adicionadosObject.prototype
ou outros protótipos intermediários que você não conhece? Nesse caso, você copiará atributos que não deve, portanto, é necessário detectar atributos imprevistos e não locais com ohasOwnProperty
métodoAlém dos atributos não enumeráveis, você encontrará um problema mais difícil ao tentar copiar objetos que possuem propriedades ocultas. Por exemplo,
prototype
é uma propriedade oculta de uma função. Além disso, o protótipo de um objeto é referenciado com o atributo__proto__
, que também está oculto e não será copiado por um loop for / in repetindo os atributos do objeto de origem. Eu acho que__proto__
pode ser específico para o intérprete JavaScript do Firefox e pode ser algo diferente em outros navegadores, mas você entendeu. Nem tudo é enumerável. Você pode copiar um atributo oculto se souber o nome dele, mas não conheço nenhuma maneira de descobri-lo automaticamente.Outro obstáculo na busca por uma solução elegante é o problema de configurar a herança do protótipo corretamente. Se o protótipo do objeto de origem for
Object
, simplesmente criar um novo objeto geral com{}
funcionará, mas se o protótipo da origem for algum descendenteObject
, você terá os membros adicionais ausentes daquele protótipo que você pulou usando ohasOwnProperty
filtro ou qual estavam no protótipo, mas não eram enumeráveis em primeiro lugar. Uma solução pode ser chamar aconstructor
propriedade do objeto de origem para obter o objeto de cópia inicial e depois copiar os atributos, mas você ainda não obterá atributos não enumeráveis. Por exemplo, umDate
objeto armazena seus dados como um membro oculto:A sequência de datas para
d1
será 5 segundos atrás da ded2
. Uma maneira de tornar umDate
igual ao outro é chamando osetTime
método, mas isso é específico daDate
classe. Eu não acho que exista uma solução geral à prova de balas para esse problema, embora eu ficaria feliz em estar errado!Quando eu tive que implementar profunda geral copiando acabei comprometer assumindo que eu só precisa copiar uma planície
Object
,Array
,Date
,String
,Number
, ouBoolean
. Os últimos 3 tipos são imutáveis, então eu poderia fazer uma cópia superficial e não me preocupar com a alteração. Eu assumi ainda que qualquer elemento contidoObject
ouArray
também seria um dos 6 tipos simples nessa lista. Isso pode ser feito com código como o seguinte:A função acima funcionará adequadamente para os 6 tipos simples que eu mencionei, desde que os dados nos objetos e matrizes formem uma estrutura em árvore. Ou seja, não há mais de uma referência aos mesmos dados no objeto. Por exemplo:
Ele não poderá manipular nenhum objeto JavaScript, mas pode ser suficiente para muitos propósitos, desde que você não assuma que ele funcionará apenas para qualquer coisa que você jogue nele.
fonte
JSON.parse(JSON.stringify([some object]),[some revirer function])
seria uma solução?var cpy = new obj.constructor()
?Se você não usar
Date
s, functions, undefined, regExp ou Infinity dentro do seu objeto, um liner muito simples éJSON.parse(JSON.stringify(object))
:Isso funciona para todos os tipos de objetos que contêm objetos, matrizes, strings, booleanos e números.
Consulte também este artigo sobre o algoritmo de clone estruturado de navegadores usado ao postar mensagens para e de um trabalhador. Ele também contém uma função para clonagem profunda.
fonte
Com o jQuery, você pode copiar superficialmente com estender :
alterações subseqüentes no
copiedObject
não afetarão ooriginalObject
e vice-versa.Ou para fazer uma cópia profunda :
fonte
var copiedObject = jQuery.extend({},originalObject);
jQuery.extend(true, {}, originalObject);
...subsequent changes to the copiedObject will not affect the originalObject, and vice versa...
. Desculpe por estar realmente confuso.No ECMAScript 6, existe o método Object.assign , que copia valores de todas as propriedades próprias enumeráveis de um objeto para outro. Por exemplo:
Mas esteja ciente de que objetos aninhados ainda são copiados como referência.
fonte
Object.assign
é o caminho a percorrer. Também é fácil preenchê-lo também: gist.github.com/rafaelrinaldi/43813e707970bd2d77faPor MDN :
Object.assign({}, a)
JSON.parse(JSON.stringify(a))
Não há necessidade de bibliotecas externas, mas é necessário verificar primeiro a compatibilidade do navegador .
fonte
clone
funcionalidade. veja mais aqui momentjs.com/docs/#/parsing/moment-cloneExistem muitas respostas, mas nenhuma menciona Object.create do ECMAScript 5, que reconhecidamente não fornece uma cópia exata, mas define a fonte como o protótipo do novo objeto.
Portanto, essa não é uma resposta exata para a pergunta, mas é uma solução de uma linha e, portanto, elegante. E funciona melhor em 2 casos:
Exemplo:
Por que considero esta solução superior? É nativo, portanto, sem loop, sem recursão. No entanto, navegadores mais antigos precisarão de um polyfill.
fonte
var a = {b:'hello',c:{d:'world'}}, b = Object.create(a); a == b /* false */; a.c == b.c /* true */;
Object.create
aponta para as propriedades dos pais por meio de referências. Isso significa que, se os valores das propriedades dos pais mudarem, a criança também mudará. Isso tem alguns efeitos colaterais surpreendentes com matrizes e objetos aninhados que podem levar a erros difíceis de encontrar no seu código, se você não souber : jsbin.com/EKivInO/2 . Um objeto clonado é um objeto completamente novo e independente que possui as mesmas propriedades e valores que o pai, mas não está conectado ao pai.Uma maneira elegante de clonar um objeto Javascript em uma linha de código
Um
Object.assign
método faz parte do padrão ECMAScript 2015 (ES6) e faz exatamente o que você precisa.Consulte Mais informação...
O polyfill para suportar navegadores mais antigos:
fonte
Object.assign
dois parâmetros são necessários quando avalue
função no polyfill usa apenas um parâmetro?arguments
objeto antes.) Estou tendo problemas para encontrarObject()
via Google ... é uma perda de qualidade, não é?Existem vários problemas com a maioria das soluções na internet. Então, decidi fazer um acompanhamento, que inclui por que a resposta aceita não deveria ser aceita.
situação inicial
Quero copiar em profundidade um Javascript
Object
com todos os seus filhos e seus filhos e assim por diante. Mas desde que eu não sou um tipo de desenvolvedor normal, a minhaObject
tem normaisproperties
,circular structures
e mesmonested objects
.Então, vamos criar um
circular structure
e umnested object
primeiro.Vamos reunir tudo em um
Object
nomea
.Em seguida, queremos copiar
a
para uma variável chamadab
e modificá-la.Você sabe o que aconteceu aqui, porque senão você nem chegaria a essa grande questão.
Agora vamos encontrar uma solução.
JSON
A primeira tentativa que tentei foi usar
JSON
.Não perca muito tempo com isso, você receberá
TypeError: Converting circular structure to JSON
.Cópia recursiva (a "resposta" aceita)
Vamos dar uma olhada na resposta aceita.
Parece bom, hein? É uma cópia recursiva do objeto e também lida com outros tipos
Date
, mas isso não era um requisito.Recursão e
circular structures
não funciona bem em conjunto ...RangeError: Maximum call stack size exceeded
solução nativa
Depois de discutir com meu colega de trabalho, meu chefe nos perguntou o que havia acontecido e ele encontrou uma solução simples depois de pesquisar no Google. É chamado
Object.create
.Esta solução foi adicionada ao Javascript há algum tempo e até lida com
circular structure
.... e veja bem, não funcionou com a estrutura aninhada dentro.
polyfill para a solução nativa
Existe um polyfill
Object.create
no navegador mais antigo, assim como o IE 8. É algo como recomendado pelo Mozilla e, é claro, não é perfeito e resulta no mesmo problema da solução nativa .Eu coloquei
F
fora do escopo para que possamos dar uma olhada no queinstanceof
nos diz.Mesmo problema que a solução nativa , mas saída um pouco pior.
a melhor solução (mas não perfeita)
Ao pesquisar, encontrei uma pergunta semelhante ( em Javascript, ao executar uma cópia em profundidade, como evito um ciclo, devido a uma propriedade ser "isto"? ), Mas com uma solução muito melhor.
E vamos dar uma olhada na saída ...
Os requisitos são atendidos, mas ainda existem alguns problemas menores, incluindo a alteração
instance
denested
ecirc
paraObject
.conclusão
A última solução usando recursão e um cache, pode não ser o melhor, mas é um verdadeiro deep-cópia do objeto. Ele lida com simples
properties
,circular structures
enested object
, mas vai atrapalhar a instância deles durante a clonagem.jsfiddle
fonte
Se você concorda com uma cópia superficial, a biblioteca underscore.js possui um método clone .
ou você pode estendê-lo como
fonte
OK, imagine que você tem este objeto abaixo e deseja cloná-lo:
ou
a resposta depende principalmente de qual ECMAscript você está usando,
ES6+
pode simplesmente usarObject.assign
o clone:ou usando o operador spread como este:
Mas se você estiver usando
ES5
, poderá usar alguns métodos, mas oJSON.stringify
, apenas certifique-se de não usar uma grande quantidade de dados para copiar, mas em muitos casos pode ser uma maneira útil de uma linha:fonte
big chunk of data
seria igual a? 100kb? 100MB? Obrigado!Object.assign
faz uma cópia superficial (assim como a propagação, @Alizera)Uma solução particularmente deselegante é usar a codificação JSON para fazer cópias profundas de objetos que não possuem métodos de membro. A metodologia é codificar JSON seu objeto de destino e, ao decodificá-lo, você obtém a cópia que está procurando. Você pode decodificar quantas vezes quiser para fazer quantas cópias precisar.
Obviamente, as funções não pertencem ao JSON, portanto, isso funciona apenas para objetos sem métodos de membro.
Essa metodologia foi perfeita para o meu caso de uso, pois estou armazenando blobs JSON em um armazenamento de valor-chave e, quando eles são expostos como objetos em uma API JavaScript, cada objeto na verdade contém uma cópia do estado original do objeto, para que possamos pode calcular o delta após o chamador ter alterado o objeto exposto.
fonte
{ 'foo': function() { return 1; } }
é um objeto construído literalmente.Você pode simplesmente usar uma propriedade de spread para copiar um objeto sem referências. Mas tenha cuidado (consulte os comentários), a 'cópia' está apenas no nível mais baixo de objeto / matriz. Propriedades aninhadas ainda são referências!
Clone completo:
Clone com referências no segundo nível:
Na verdade, o JavaScript não suporta nativamente clones profundos. Use uma função utilitária. Por exemplo Ramda:
fonte
const first = {a: 'foo', b: 'bar'}; const second = {...{a} = first}
Para aqueles que usam AngularJS, também há um método direto para clonar ou estender os objetos nesta biblioteca.
ou
Mais na documentação do angular.copy ...
fonte
Aqui está uma função que você pode usar.
fonte
new
. A resposta aceita não.A. A resposta de Lucy está quase completa, aqui está minha pequena contribuição: existe uma maneira de lidar com referências recursivas , veja esta linha
if(this[attr]==this) copy[attr] = copy;
Se o objeto for XML elemento DOM, devemos usar cloneNode vez
if(this.cloneNode) return this.cloneNode(true);
Inspirado no estudo exaustivo de A.Levy e na abordagem de prototipagem de Calvin, ofereço esta solução:
Veja também a nota de Andy Burke nas respostas.
fonte
Date.prototype.clone = function() {return new Date(+this)};
Neste artigo: Como copiar matrizes e objetos em Javascript por Brian Huisman:
fonte
var copiedObj = Object.create(obj);
uma ótima maneira também?No ES-6, você pode simplesmente usar Object.assign (...). Ex:
Uma boa referência está aqui: https://googlechrome.github.io/samples/object-assign-es6/
fonte
let obj = {person: 'Thor Odinson'}; let clone = Object.assign({}, obj); clone.title = "Whazzup";
)obj
propriedades de fato são clonados. Os valores da propriedade que são os próprios objetos não serão clonados.No ECMAScript 2018
Esteja ciente de que objetos aninhados ainda são copiados como referência.
fonte
const objDeepClone = JSON.parse(JSON.stringify(obj));
Usando o Lodash:
fonte
_.cloneDeep(x)
pois é essencialmente a mesma coisa que acima, mas lê melhor.Interessado em clonar objetos simples:
JSON.parse(JSON.stringify(json_original));
Fonte: Como copiar o objeto JavaScript para a nova variável, NÃO por referência?
fonte
Você pode clonar um objeto e remover qualquer referência da anterior usando uma única linha de código. Simplesmente faça:
Para navegadores / mecanismos que atualmente não oferecem suporte ao Object.create, você pode usar este polyfill:
fonte
Object.create(...)
parece definitivamente o caminho a percorrer.Object.hasOwnProperty
? Dessa forma, as pessoas sabem como impedir a pesquisa no link do protótipo.text
membro no obj2. Você não está fazendo uma cópia, apenas adiando para a cadeia de protótipos quando um membro não for encontrado no obj2.Nova resposta para uma pergunta antiga! Se você tem o prazer de usar o ECMAScript 2016 (ES6) com a Spread Syntax , é fácil.
Isso fornece um método limpo para uma cópia superficial de um objeto. Fazer uma cópia profunda, significando criar uma nova cópia de todos os valores em todos os objetos aninhados recursivamente, requer uma das soluções mais pesadas acima.
JavaScript continua evoluindo.
fonte
var obj = {'key1': 'value1'};
var array = [...obj]; // TypeError: obj is not iterable
Solução ES6 se você deseja (superficial) clonar uma instância de classe e não apenas um objeto de propriedade.
fonte
let cloned = Object.assign({}, obj)
?Eu acho que há uma resposta simples e funcional. Na cópia profunda, existem duas preocupações:
Então, acho que uma solução simples será primeiro serializar e desserializar e, em seguida, fazer uma atribuição para copiar funções também.
Embora esta pergunta tenha muitas respostas, espero que esta também ajude.
fonte
cloneDeep
.Para uma cópia profunda e clone, JSON.stringify e JSON.parse o objeto:
fonte
Esta é uma adaptação do código de A. Levy para também manipular a clonagem de funções e referências múltiplas / cíclicas - o que isso significa é que, se duas propriedades na árvore clonada forem referências do mesmo objeto, a árvore de objetos clonados terá essas As propriedades apontam para um e o mesmo clone do objeto referenciado. Isso também resolve o caso de dependências cíclicas que, se não tratadas, levam a um loop infinito. A complexidade do algoritmo é O (n)
Alguns testes rápidos
fonte
Eu só queria adicionar a todos
Object.create
soluções neste post que isso não funciona da maneira desejada com o nodejs.No Firefox, o resultado de
é
{test:"test"}
.Em nodejs é
fonte
Object.hasOwnProperty
para verificar se você é o proprietário ou não da matriz. Sim, isso adiciona complexidade adicional para lidar com a herança prototípica.fonte
if(!src && typeof src != "object"){
. Eu acho que||
não deveria&&
.Como o mindeavor afirmou que o objeto a ser clonado é um objeto 'literalmente construído', uma solução pode ser simplesmente gerar o objeto várias vezes, em vez de clonar uma instância do objeto:
fonte
Eu escrevi minha própria implementação. Não tenho certeza se isso conta como uma solução melhor:
A seguir está a implementação:
fonte