Qual é a maneira mais eficiente de clonar um objeto JavaScript? Vi obj = eval(uneval(o));
ser usado, mas isso não é padrão e é suportado apenas pelo Firefox .
Eu fiz coisas como, obj = JSON.parse(JSON.stringify(o));
mas questiono a eficiência.
Também vi funções de cópia recursiva com várias falhas.
Estou surpreso que não exista solução canônica.
javascript
object
clone
jschrab
fonte
fonte
eval()
geralmente é uma má ideia, porque muitos otimizadores de mecanismo Javascript precisam ser desativados ao lidar com variáveis definidas viaeval
. Apenas tereval()
seu código pode levar a um desempenho pior.JSON
método perderá todos os tipos de Javascript que não têm equivalente em JSON. Por exemplo:JSON.parse(JSON.stringify({a:null,b:NaN,c:Infinity,d:undefined,e:function(){},f:Number,g:false}))
irá gerar{a: null, b: null, c: null, g: false}
Respostas:
Clonagem profunda nativa
É chamado de "clonagem estruturada", funciona experimentalmente no nó 11 e posterior e, esperançosamente, chegará aos navegadores. Veja esta resposta para mais detalhes.
Clonagem rápida com perda de dados - JSON.parse / stringify
Se você não usar
Date
s, funçõesundefined
,Infinity
, regexps, Mapas, Conjuntos, gotas, filelists, ImageDatas, Arrays esparsos, datilografado matrizes ou outros tipos complexos dentro de seu objeto, uma forma muito simples um forro de clone profundo um objeto é:JSON.parse(JSON.stringify(object))
Veja a resposta de Corban para referências.
Clonagem confiável usando uma biblioteca
Como a clonagem de objetos não é trivial (tipos complexos, referências circulares, função etc.), a maioria das principais bibliotecas fornece função para clonar objetos. Não reinvente a roda - se você já estiver usando uma biblioteca, verifique se ela possui uma função de clonagem de objetos. Por exemplo,
cloneDeep
; pode ser importado separadamente por meio do módulo lodash.clonedeep e provavelmente é a melhor opção se você ainda não estiver usando uma biblioteca que fornece uma função de clonagem profundaangular.copy
jQuery.extend(true, { }, oldObject)
;.clone()
apenas clona elementos DOMES6
Para completar, observe que o ES6 oferece dois mecanismos de cópia rasos:
Object.assign()
e a sintaxe de propagação . que copia valores de todas as propriedades próprias enumeráveis de um objeto para outro. Por exemplo:fonte
.clone()
, que não é o código certo a ser usando neste contexto). Infelizmente, essa questão passou por tantas revisões que a discussão original não é mais aparente! Por favor, siga os conselhos de Corban e escreva um loop ou copie as propriedades diretamente para um novo objeto, se você se preocupa com a velocidade. Ou teste você mesmo!Faça o checkout deste benchmark: http://jsben.ch/#/bWfk9
Nos meus testes anteriores, onde a velocidade era a principal preocupação que encontrei
para ser a maneira mais lenta de clonar profundamente um objeto (é mais lento que o jQuery.extend com o
deep
sinalizador definido como verdadeiro de 10 a 20%).O jQuery.extend é muito rápido quando o
deep
sinalizador é definido comofalse
(clone superficial). É uma boa opção, pois inclui alguma lógica extra para validação de tipo e não copia propriedades indefinidas, etc., mas isso também o torna um pouco mais lento.Se você conhece a estrutura dos objetos que está tentando clonar ou pode evitar matrizes aninhadas profundas, pode escrever um
for (var i in obj)
loop simples para clonar seu objeto enquanto verifica hasOwnProperty, e será muito mais rápido que o jQuery.Por fim, se você estiver tentando clonar uma estrutura de objeto conhecida em um hot loop, poderá obter MUITO MAIS DESEMPENHO simplesmente alinhando o procedimento de clonagem e construindo manualmente o objeto.
Os mecanismos de rastreamento JavaScript são
for..in
ruins para otimizar loops e a verificação de hasOwnProperty também o tornará lento. Clone manual quando a velocidade é uma necessidade absoluta.Cuidado ao usar o
JSON.parse(JSON.stringify(obj))
método emDate
objetos -JSON.stringify(new Date())
retorna uma representação de string da data no formato ISO, queJSON.parse()
não é convertida novamente em umDate
objeto. Veja esta resposta para mais detalhes .Além disso, observe que, pelo menos no Chrome 65, a clonagem nativa não é o caminho a percorrer. De acordo com o JSPerf, executar a clonagem nativa criando uma nova função é quase 800x mais lenta que o uso do JSON.stringify, que é incrivelmente rápido em todo o caminho.
Atualização para ES6
Se você estiver usando o Javascript ES6, tente este método nativo para clonagem ou cópia superficial.
fonte
keys
seuobject
, que temfunctions
como seus valores, porque oJSON
não suporta funções.JSON.parse(JSON.stringify(obj))
de objetos de data também converterá a data em UTC na representação de sequência no formato ISO8601 .Supondo que você tenha apenas variáveis e não funções no seu objeto, você pode apenas usar:
fonte
JSON
é implementado no código nativo (na maioria dos navegadores), isso será consideravelmente mais rápido do que qualquer outra solução de cópia profunda baseada em javascript e às vezes pode ser mais rápido que uma técnica de cópia superficial baseada em javascript (consulte: jsperf.com/cloning -an-object / 79 ).JSON.stringify({key: undefined}) //=> "{}"
Date
objetos armazenados dentro do objeto, convertendo-os em forma de string.Clonagem Estruturada
O padrão HTML inclui um algoritmo interno de clonagem / serialização estruturado que pode criar clones profundos de objetos. Ele ainda é limitado a certos tipos internos, mas, além dos poucos tipos suportados pelo JSON, também suporta Datas, RegExps, Mapas, Conjuntos, Blobs, FileLists, ImageDatas, Matrizes esparsas, Matrizes digitadas e provavelmente mais no futuro . Ele também preserva referências nos dados clonados, permitindo suportar estruturas cíclicas e recursivas que causariam erros no JSON.
Suporte no Node.js: experimental 🙂
O
v8
módulo no Node.js atualmente (a partir do Nó 11) expõe diretamente a API de serialização estruturada , mas essa funcionalidade ainda está marcada como "experimental" e está sujeita a alterações ou remoção em versões futuras. Se você estiver usando uma versão compatível, a clonagem de um objeto é tão simples quanto:Suporte direto em navegadores: talvez eventualmente? 😐
Atualmente, os navegadores não fornecem uma interface direta para o algoritmo de clonagem estruturada, mas uma
structuredClone()
função global foi discutida no whatwg / html # 793 no GitHub . Conforme proposto atualmente, usá-lo para a maioria dos propósitos seria tão simples quanto:A menos que isso seja enviado, as implementações de clones estruturados dos navegadores são expostas apenas indiretamente.
Solução alternativa assíncrona: utilizável. 😕
A maneira mais baixa de criar um clone estruturado com APIs existentes é postar os dados através de uma porta de um MessageChannels . A outra porta emitirá um
message
evento com um clone estruturado do anexo.data
. Infelizmente, a escuta desses eventos é necessariamente assíncrona, e as alternativas síncronas são menos práticas.Exemplo de uso:
Soluções síncronas: péssimas! 🤢
Não há boas opções para criar clones estruturados de forma síncrona. Aqui estão alguns truques impraticáveis.
history.pushState()
ehistory.replaceState()
ambos criam um clone estruturado de seu primeiro argumento e atribuem esse valor ahistory.state
. Você pode usar isso para criar um clone estruturado de qualquer objeto como este:Exemplo de uso:
Mostrar snippet de código
Embora síncrono, isso pode ser extremamente lento. Incorre em toda a sobrecarga associada à manipulação do histórico do navegador. Se você chamar esse método repetidamente, o Chrome ficará temporariamente sem resposta.
O
Notification
construtor cria um clone estruturado de seus dados associados. Ele também tenta exibir uma notificação do navegador para o usuário, mas isso falhará silenciosamente, a menos que você tenha solicitado permissão de notificação. Caso você tenha permissão para outros fins, fecharemos imediatamente a notificação que criamos.Exemplo de uso:
Mostrar snippet de código
fonte
history.pushState()
ehistory.replaceState()
definem de forma síncronahistory.state
um clone estruturado do primeiro argumento. Um pouco estranho, mas funciona. Estou atualizando minha resposta agora.Se não houver nenhum, você pode tentar:
fonte
A maneira eficiente de clonar (não clonar profundamente) um objeto 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
Código:
Teste:
fonte
var obj = {}
eobj.a = obj
from.constructor
sejaDate
por exemplo. Como o terceiroif
teste seria alcançado quando o segundoif
teste fosse bem-sucedido e faria com que a função retornasse (desdeDate != Object && Date != Array
)?Isto é o que estou usando:
fonte
cloneObject({ name: null })
=>{"name":{}}
typeof null > "object"
, masObject.keys(null) > TypeError: Requested keys of a value that is not an object.
mudar a condição deif(typeof(obj[i])=="object" && obj[i]!=null)
Cópia profunda por desempenho: classificada da melhor à pior
Copie em profundidade uma matriz de seqüências de caracteres ou números (um nível - sem ponteiros de referência):
Quando uma matriz contém números e seqüências de caracteres - funções como .slice (), .concat (), .splice (), o operador de atribuição "=" e a função de clone do Underscore.js; fará uma cópia profunda dos elementos da matriz.
Onde a reatribuição tem o desempenho mais rápido:
E .slice () tem melhor desempenho que .concat (), http://jsperf.com/duplicate-array-slice-vs-concat/3
Cópia profunda de uma matriz de objetos (dois ou mais níveis - ponteiros de referência):
Escreva uma função personalizada (com desempenho mais rápido que $ .extend () ou JSON.parse):
Use funções utilitárias de terceiros:
Onde $ .extend do jQuery tem melhor desempenho:
fonte
out[key] = (typeof v === "object" && v !== null) ? copy(v) : v;
fonte
Objetos de cópia profunda em JavaScript (acho o melhor e o mais simples)
1. Usando JSON.parse (JSON.stringify (objeto));
2.Usando o método criado
3. Usando o link _.cloneDeep do Lo-Dash
4. Usando o método Object.assign ()
MAS ERRADO QUANDO
5.Utilizando o link Underscore.js _.clone Underscore.js
MAS ERRADO QUANDO
JSBEN.CH Playground de Benchmarking de Desempenho 1 ~ 3 http://jsben.ch/KVQLd
fonte
Object.assign()
não executar uma cópia profundalet data = {title:["one", "two"]}; let tmp = cloneObject(data); tmp.title.pop();
-lo jogar:TypeError: tmp.title.pop is not a function
(claro pop () multa funciona se eu sódo let tmp = data
, mas então eu não posso modificar tmp sem afetar os dados)Há uma biblioteca (chamada “clone”) , que faz isso muito bem. Ele fornece a clonagem / cópia recursiva mais completa de objetos arbitrários que eu conheço. Ele também suporta referências circulares, que ainda não são cobertas pelas outras respostas.
Você pode encontrá-lo também nas npm . Pode ser usado para o navegador e também para o Node.js.
Aqui está um exemplo de como usá-lo:
Instale-o com
ou empacotá-lo com Ender .
Você também pode baixar o código-fonte manualmente.
Então você pode usá-lo no seu código fonte.
(Aviso: sou o autor da biblioteca.)
fonte
JSON.parse(JSON.stringify(obj))
?Cloning
Um objeto sempre foi uma preocupação em JS, mas já existia antes do ES6, listo diferentes maneiras de copiar um objeto em JavaScript abaixo, imagine que você tenha o objeto abaixo e gostaria de ter uma cópia profunda disso:Existem algumas maneiras de copiar este objeto, sem alterar a origem:
1) ES5 +, usando uma função simples para fazer a cópia para você:
2) ES5 +, usando JSON.parse e JSON.stringify.
3) AngularJs:
4) jQuery:
5) SublinhadoJs & Loadash:
Espero que estas ajudem ...
fonte
Object.assign
se não copiar profundo. Exemplo:var x = { a: { b: "c" } }; var y = Object.assign({}, x); x.a.b = "d"
. Se essa fosse uma cópia profunda,y.a.b
ainda seriac
, mas é agorad
.Sei que este é um post antigo, mas achei que isso poderia ser de alguma ajuda para a próxima pessoa que tropeçar.
Contanto que você não atribua um objeto a nada, ele não mantém referência na memória. Portanto, para criar um objeto que você deseja compartilhar entre outros objetos, você precisará criar uma fábrica como esta:
fonte
const defaultFoo = { a: { b: 123 } };
você pode irconst defaultFoo = () => ({ a: { b: 123 } };
e seu problema está resolvido. No entanto, realmente não é uma resposta para a pergunta. Pode ter feito mais sentido como um comentário sobre a pergunta, não uma resposta completa.Se você estiver usando, a biblioteca Underscore.js possui um método clone .
fonte
.clone(...)
método de uma biblioteca de utilitários . Todas as principais bibliotecas as possuem e as breves e repetidas respostas não detalhadas não são úteis para a maioria dos visitantes, que não usarão essa biblioteca em particular.Aqui está uma versão da resposta de ConroyP acima que funciona mesmo que o construtor tenha exigido parâmetros:
Esta função também está disponível na minha biblioteca simpleoo .
Editar:
Aqui está uma versão mais robusta (graças a Justin McCandless, agora isso também suporta referências cíclicas):
fonte
A seguir, são criadas duas instâncias do mesmo objeto. Eu encontrei e estou usando atualmente. É simples e fácil de usar.
fonte
Crockford sugere (e eu prefiro) usar esta função:
É conciso, funciona como esperado e você não precisa de uma biblioteca.
EDITAR:
Este é um polyfill para
Object.create
, portanto, você também pode usá-lo.NOTA: Se você usar um pouco disso, poderá ter problemas com alguma iteração usada
hasOwnProperty
. Porque,create
crie um novo objeto vazio que herdaoldObject
. Mas ainda é útil e prático para clonar objetos.Por exemplo, se
oldObject.a = 5;
mas:
fonte
var extendObj = function(childObj, parentObj) { var tmpObj = function () {} tmpObj.prototype = parentObj.prototype; childObj.prototype = new tmpObj(); childObj.prototype.constructor = childObj; };
... davidshariff.com/blog/javascript-inheritance-patternsO Lodash possui um bom método _.cloneDeep (value) :
fonte
.clone(...)
método de uma biblioteca de utilitários . Todas as principais bibliotecas as possuem e as breves e repetidas respostas não detalhadas não são úteis para a maioria dos visitantes, que não usarão essa biblioteca em particular._.merge({}, objA)
. Se pelo menos o lodash não modificasse objetos, aclone
função não seria necessária.fonte
Cópia única de uma cópia ( ECMAScript 5th edition ):
Uma cópia simples e rasa ( ECMAScript 6ª edição , 2015):
fonte
Object.keys
lo, ignora as propriedades não enumeráveis e herdadas. Além disso, ele perde os descritores de propriedade ao fazer a atribuição direta.Só porque eu não vi o AngularJS mencionado e pensei que as pessoas poderiam querer saber ...
angular.copy
também fornece um método de cópia profunda de objetos e matrizes.fonte
angular.extend({},obj);
jQuery.extend
eangular.extend
são cópias rasas.angular.copy
é uma cópia profunda.Parece ainda não haver um operador de clone profundo ideal para objetos do tipo array. Como o código abaixo ilustra, o clonador jQuery de John Resig transforma matrizes com propriedades não numéricas em objetos que não são matrizes e o clonador JSON do RegDwight remove as propriedades não numéricas. Os testes a seguir ilustram esses pontos em vários navegadores:
fonte
Eu tenho duas boas respostas, dependendo se seu objetivo é clonar um "objeto JavaScript antigo simples" ou não.
Vamos supor também que sua intenção é criar um clone completo sem referências de protótipo para o objeto de origem. Se você não estiver interessado em um clone completo, poderá usar muitas das rotinas Object.clone () fornecidas em algumas das outras respostas (padrão de Crockford).
Para objetos JavaScript simples e antigos, uma boa maneira de clonar um objeto em tempos de execução modernos é muito simples:
Observe que o objeto de origem deve ser um objeto JSON puro. Isto é, todas as suas propriedades aninhadas devem ser escalares (como booleano, string, array, objeto, etc.). Quaisquer funções ou objetos especiais como RegExp ou Date não serão clonados.
É eficiente? Claro que sim. Tentamos todos os tipos de métodos de clonagem e isso funciona melhor. Tenho certeza que algum ninja poderia invocar um método mais rápido. Mas suspeito que estamos falando de ganhos marginais.
Essa abordagem é simples e fácil de implementar. Envolva-o em uma função de conveniência e, se você realmente precisar obter algum ganho, tente mais tarde.
Agora, para objetos JavaScript não comuns, não há uma resposta realmente simples. De fato, não pode haver por causa da natureza dinâmica das funções JavaScript e do estado interno do objeto. A clonagem profunda de uma estrutura JSON com funções internas requer que você recrie essas funções e seu contexto interno. E o JavaScript simplesmente não tem uma maneira padronizada de fazer isso.
A maneira correta de fazer isso, mais uma vez, é através de um método de conveniência que você declara e reutiliza dentro do seu código. O método de conveniência pode ser dotado de alguma compreensão de seus próprios objetos, para que você possa recriar adequadamente o gráfico dentro do novo objeto.
Nós escrevemos por conta própria, mas a melhor abordagem geral que já vi é abordada aqui:
http://davidwalsh.name/javascript-clone
Essa é a ideia certa. O autor (David Walsh) comentou a clonagem de funções generalizadas. Isso é algo que você pode optar por fazer, dependendo do seu caso de uso.
A idéia principal é que você precisa lidar especialmente com a instanciação de suas funções (ou classes prototípicas, por assim dizer) por tipo. Aqui, ele forneceu alguns exemplos para RegExp e Date.
Este código não é apenas breve, mas também é muito legível. É bem fácil de estender.
Isso é eficiente? Claro que sim. Como o objetivo é produzir um verdadeiro clone de cópia profunda, você precisará percorrer os membros do gráfico do objeto de origem. Com essa abordagem, você pode ajustar exatamente quais membros filhos tratar e como lidar manualmente com tipos personalizados.
Então lá vai você. Duas abordagens. Ambos são eficientes na minha opinião.
fonte
Geralmente, essa não é a solução mais eficiente, mas faz o que eu preciso. Casos de teste simples abaixo ...
Teste de matriz cíclica ...
Teste de funcionamento...
fonte
AngularJS
Bem, se você estiver usando angular, você também pode fazer isso
fonte
Discordo da resposta com os melhores votos aqui . Um Deep Clone recursivo é muito mais rápido que a abordagem JSON.parse (JSON.stringify (obj)) mencionada.
E aqui está a função para referência rápida:
fonte
if(o instanceof Date) return new Date(o.valueOf());
depois de verificar se existe um valor nulo `fonte
Somente quando você pode usar o ECMAScript 6 ou transpilers .
Recursos:
Código:
fonte
Aqui está um método abrangente clone () que pode clonar qualquer objeto JavaScript. Ele lida com quase todos os casos:
fonte