Qual é a maneira mais eficiente de clonar profundamente um objeto em javascript?

Qual é a maneira mais eficiente de clonar um objeto JavaScript? Eu tenho visto obj = eval(uneval(o)); mas que não é padrão e é suportado apenas pelo Firefox .

Eu fiz coisas como obj = JSON.parse(JSON.stringify(o)); , mas duvido da eficácia.

Eu também vi as funções de cópia recursivas com várias falhas.

Surpreende-me que não exista uma solução canônica.

4845
23 сент. definido por jschrab em 23 set . 2008-09-23 19:26 '08 em 7:26 pm 2008-09-23 19:26
@ 69 respostas
  • 1
  • 2
  • 3

Nota: Esta é a resposta para outra resposta, não a resposta correta para esta pergunta. Se você quiser clonar objetos rapidamente, siga o conselho do Corban em sua resposta a esta pergunta.


Gostaria de observar que o método .clone() no jQuery apenas clona elementos DOM. Para clonar objetos JavaScript, você deve:

 // Shallow copy var newObject = jQuery.extend({}, oldObject); // Deep copy var newObject = jQuery.extend(true, {}, oldObject); 

Mais informações podem ser encontradas na documentação do jQuery .

Eu também quero salientar que uma cópia profunda é realmente muito mais inteligente do que a mostrada acima - ela pode evitar muitas armadilhas (por exemplo, expandir profundamente o elemento DOM). É frequentemente usado no núcleo do jQuery e em plugins com grande efeito.

4203
23 сент. Resposta dada por John Resig em 23 Set 2008-09-23 21:09 '08 em 9:09 pm 2008-09-23 21:09

Confira este benchmark: http://jsben.ch/#/bWfk9

Nos meus testes anteriores, onde a velocidade era o principal problema, descobri

<Anterior> <code> JSON.parse (JSON.stringify (OBJ)) Código>

para ser o caminho mais rápido para clonar profundamente um objeto (ele supera jQuery.extend com o sinalizador deep configurado em 10-20%).

jQuery.extend é bastante rápido quando o sinalizador de valor profundo é definido como falso (clone superficial). Esta é uma boa opção, pois inclui lógica adicional para verificação de tipos e não copia as propriedades de undefined, etc. Mas também irá atrasá-lo um pouco.

Se você conhece a estrutura dos objetos que está tentando clonar ou pode evitar matrizes aninhadas profundas, é possível escrever um loop for (var я in obj) simples for (var я in obj) para clonar seu objeto, verificando hasOwnProperty e ele será muito mais rápido que jQuery.

border=0

Finalmente, se você estiver tentando clonar uma estrutura de objeto conhecida em um loop quente, poderá obter MUITO MAIS MAIS DESEMPENHO simplesmente inserindo o procedimento de clonagem e criando manualmente o objeto.

Os mecanismos de rastreamento de JavaScript não otimizam os loops em..in e a verificação de hasOwnProperty também o atrasará. Clone manual quando a velocidade é uma necessidade absoluta.

  var clonedObject = { knownProp: obj.knownProp,.. } Код> 

Cuidado com o uso do método JSON.parse(JSON.stringify(obj)) para objetos Date - JSON.stringify(новая дата()) retorna uma representação de string da data no formato ISO na qual JSON.parse() não retorna ao objeto Date . Veja esta resposta para mais detalhes .

Além disso, observe que no Chrome 65, pelo menos, a clonagem nativa não é adequada. De acordo com este JSPerf , fazer sua própria clonagem criando uma nova função é quase 800 vezes mais lento do que usando o JSON.stringify, que passa incrivelmente rápido por toda a placa.

2046
17 марта '11 в 22:19 2011-03-17 22:19 a resposta é dada por Corban Brook em 17 de março de 2011 às 22:19 de 2011-03-17 22:19

Supondo que você tenha apenas variáveis ​​e não funções no seu objeto, você pode simplesmente usar:

 var newObject = JSON.parse(JSON.stringify(oldObject)); 
431
04 янв. Resposta é dada pelo Sultão Shakir 04 de janeiro 2011-01-04 11:05 '11 às 11:05 2011-01-04 11:05

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 de vários tipos suportados pelo JSON, também suporta Datas, RegExps, Mapas, Conjuntos, Blobs, FileLists, ImageDatas, matrizes esparsas, Arrays digitados e possivelmente mais no futuro. ., Ele também armazena referências nos dados clonados, o que permite manter estruturas cíclicas e recursivas que podem causar erros para JSON.

Suporte no Node.js: experimental 🙂

O módulo v8 no Node.js atualmente (como no Nó 11) fornece diretamente uma API de serialização estruturada , mas essa funcionalidade ainda é marcada como “experimental” e pode ser alterada ou removida em versões futuras. Se você estiver usando uma versão compatível, clonar um objeto é tão simples quanto:

06 июня '12 в 17:59 2012-06-06 17:59 a resposta é dada a Jeremy Banks 06 de junho '12 at 17:59 2012-06-06 17:59

Se não fosse incorporado, você poderia tentar:

 function clone(obj) { if (obj === null || typeof (obj) !== 'object' || 'isActiveClone' in obj) return obj; if (obj instanceof Date) var temp = new obj.constructor(); //or new Date(obj); else var temp = obj.constructor(); for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { obj['isActiveClone'] = null; temp[key] = clone(obj[key]); delete obj['isActiveClone']; } } return temp; } 
294
23 сент. Resposta dada por ConroyP em 23 Set 2008-09-23 19:38 '08 at 7:38 pm 2008-09-23 19:38

Maneira eficaz de clonar (não clonagem profunda) um objeto em uma linha de código

O método Object.assign faz parte do padrão ECMAScript 2015 (ES6) e faz exatamente o que você precisa.

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

O método Object.assign () é usado para copiar os valores de todas as propriedades apropriadas enumeradas de um ou mais objetos de origem para o objeto de destino.

Leia mais ...

polyfill para suportar navegadores mais antigos:

 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; } }); } 
148
15 дек. A resposta é dada por Eugene Tiurin 15 de dezembro 2015-12-15 10:26 15h às 10h26 de 2015-12-15 10:26

código:

 // extends 'from' object with members from 'to'. If 'to' is null, a deep clone of 'from' is returned function extend(from, to) { if (from == null || typeof from != "object") return from; if (from.constructor != Object  from.constructor != Array) return from; if (from.constructor == Date || from.constructor == RegExp || from.constructor == Function || from.constructor == String || from.constructor == Number || from.constructor == Boolean) return new from.constructor(from); to = to || new from.constructor(); for (var name in from) { to[name] = typeof to[name] == "undefined" ? extend(from[name], null) : to[name]; } return to; } 

Teste:

 var obj = { date: new Date(), func: function(q) { return 1 + q; }, num: 123, text: "asdasd", array: [1, "asd"], regex: new RegExp(/aaa/i), subobj: { num: 234, text: "asdsaD" } } var clone = extend(obj); 
94
25 июня '09 в 10:53 2009-06-25 10:53 a resposta é dada por Kamarey em 09 de junho de 2009 às 10:53 2009-06-25 10:53

Isso é o que eu uso:

 function cloneObject(obj) { var clone = {}; for(var i in obj) { if(typeof(obj[i])=="object"  obj[i] != null) clone[i] = cloneObject(obj[i]); else clone[i] = obj[i]; } return clone; } 
86
12 дек. A resposta é dada Alan 12 de dezembro 2009-12-12 01:47 '09 às 1:47 2009-12-12 01:47

Desempenho de cópia profunda: do melhor ao pior

  • Reatribuição "=" (matrizes de strings, matrizes numéricas - somente)
  • Fatia (matrizes de string, matrizes de números - somente)
  • Concatenação (apenas matrizes de cadeias, matrizes numéricas)
  • Função personalizada: para loop ou cópia recursiva
  • jQuery $ .extend
  • JSON.parse (apenas matrizes de cadeias, matrizes de números, matrizes de objetos)
  • Underscore.js _.clone (apenas matrizes de cadeias, matrizes numéricas)
  • Lo-Dash _.cloneDeep

Copie profundamente uma matriz de strings ou números (um nível - sem ponteiros):

Quando um array contém números e strings - funções como.slice (), Concat (),. Splice (), o operador de atribuição "=" e a função clone Underscore.js; faça uma cópia profunda dos elementos da matriz.

Se a reclassificação tiver o maior desempenho:

 var arr1 = ['a', 'b', 'c']; var arr2 = arr1; arr1 = ['a', 'b', 'c']; 

I.slice () tem melhor desempenho que .concat (), http://jsperf.com/duplicate-array-slice-vs-concat/3

 var arr1 = ['a', 'b', 'c']; // Becomes arr1 = ['a', 'b', 'c'] var arr2a = arr1.slice(0); // Becomes arr2a = ['a', 'b', 'c'] - deep copy var arr2b = arr1.concat(); // Becomes arr2b = ['a', 'b', 'c'] - deep copy 

Copie profundamente uma matriz de objetos (dois ou mais níveis - ponteiros):

 var arr1 = [{object:'a'}, {object:'b'}]; 

Escreva uma função personalizada (tem melhor desempenho do que $ .extend () ou JSON.parse):

 function copy(o) { var out, v, key; out = Array.isArray(o) ? [] : {}; for (key in o) { v = o[key]; out[key] = (typeof v === "object"  v !== null) ? copy(v) : v; } return out; } copy(arr1); 

Use funções de utilitário de terceiros:

 $.extend(true, [], arr1); // Jquery Extend JSON.parse(arr1); _.cloneDeep(arr1); // Lo-dash 

Onde jQuery $ .extend tem melhor desempenho:

71
18 сент. A resposta é dada em 18 de setembro . 2014-09-18 23:10 '14 às 23:10 2014-09-18 23:10
 var clone = function() { var newObj = (this instanceof Array) ? [] : {}; for (var i in this) { if (this[i]  typeof this[i] == "object") { newObj[i] = this[i].clone(); } else { newObj[i] = this[i]; } } return newObj; }; Object.defineProperty( Object.prototype, "clone", {value: clone, enumerable: false}); 
60
26 дек. a resposta é dada por Zibri em 26 de dezembro. 2009-12-26 17:59 '09 às 17:59 2009-12-26 17:59

Eu sei que este é um post antigo, mas achei que poderia ajudar alguém que tropeça.

Contanto que você não atribua um objeto a algo, ele não mantém uma referência na memória. Assim, para criar um objeto que você deseja compartilhar entre outros objetos, você precisa criar uma fábrica como esta:

 var a = function(){ return { father:'zacharias' }; }, b = a(), c = a(); c.father = 'johndoe'; alert(b.father); 
53
24 сент. resposta dada por Joe 24 set. 2011-09-24 22:28 '11 às 22:28 2011-09-24 22:28

Objeto de Cloning sempre foi uma questão de preocupação em JS, mas foi tudo antes ES6, eu listo várias maneiras de copiar um objeto em JavaScript abaixo, imagine que você tem um objeto abaixo e você quer ter uma cópia profunda do que:

 var obj = {a:1, b:2, c:3, d:4}; 

Existem várias maneiras de copiar este objeto sem alterar a fonte:

1) ES5 + usando uma função simples para copiar:

 function deepCopyObj(obj) { if (null == obj || "object" != typeof obj) return obj; if (obj instanceof Date) { var copy = new Date(); copy.setTime(obj.getTime()); return copy; } if (obj instanceof Array) { var copy = []; for (var i = 0, len = obj.length; i < len; i++) { copy[i] = cloneSO(obj[i]); } return copy; } 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 this object."); } 

2) ES5 + usando JSON.parse e JSON.stringify.

 var deepCopyObj = JSON.parse(JSON.stringify(obj)); 

3) AngularJs:

 var deepCopyObj = angular.copy(obj); 

4) jQuery:

 var deepCopyObj = jQuery.extend(true, {}, obj); 

5) SublinhadoJs e Loadash:

 var deepCopyObj = _.cloneDeep(obj); //latest version UndescoreJs makes shallow copy 

Espero que esta ajuda ...

52
03 апр. A resposta é dada Alireza 03 abr. 2017-04-03 18:37 '17 às 18:37 2017-04-03 18:37

Há uma biblioteca (chamada "clone") , o que a torna muito boa. Ele fornece a mais completa clonagem / cópia recursiva de objetos arbitrários que eu conheço. Ele também suporta links circulares que ainda não são cobertos por outras respostas.

Você pode encontrá-lo no npm . Pode ser usado tanto para o navegador quanto para o Node.js.

Aqui está um exemplo de como usá-lo:

Instale-o com

 npm install clone 

ou empacote com Ender .

 ender build clone [...] 

Você também pode baixar o código-fonte manualmente.

Então você pode usá-lo em seu código-fonte.

 var clone = require('clone'); var a = { foo: { bar: 'baz' } }; // inital value of a var b = clone(a); // clone a -> b a.foo.bar = 'foo'; // change a console.log(a); // { foo: { bar: 'foo' } } console.log(b); // { foo: { bar: 'baz' } } 

(Isenção: Sou o autor da biblioteca.)

51
17 окт. a resposta é dada pvorb 17 oct. 2012-10-17 21:36 '12 às 21:36 2012-10-17 21:36

Se você usá-lo, a biblioteca Underscore.js tem um clone .

 var newObject = _.clone(oldObject); 
48
15 дек. a resposta é dada por itsadok 15 dez. 2011-12-15 18:56 '11 às 18:56 2011-12-15 18:56

Aqui está a versão acima do ConroyP, que funciona mesmo se o designer exigir parâmetros:

 //If Object.create isn't already defined, we just do the simple shim, //without the second argument, since that all we need here var object_create = Object.create; if (typeof object_create !== 'function') { object_create = function(o) { function F() {} F.prototype = o; return new F(); }; } function deepCopy(obj) { if(obj == null || typeof(obj) !== 'object'){ return obj; } //make sure the returned object has the same prototype as the original var ret = object_create(obj.constructor.prototype); for(var key in obj){ ret[key] = deepCopy(obj[key]); } return ret; } 

Esse recurso também está disponível na minha biblioteca simpleoo .

Editar:

Aqui está uma versão mais confiável (graças a Justin McCandles, agora ele suporta referências circulares):

  function deepCopy(src,  _visited, _copiesVisited) { if(src === null || typeof(src) !== 'object'){ return src; } //Honor native/custom clone methods if(typeof src.clone == 'function'){ return src.clone(true); } //Special cases: //Date if(src instanceof Date){ return new Date(src.getTime()); } //RegExp if(src instanceof RegExp){ return new RegExp(src); } //DOM Element if(src.nodeType  typeof src.cloneNode == 'function'){ return src.cloneNode(true); } // Initialize the visited objects arrays if needed. // This is used to detect cyclic references. if (_visited === undefined){ _visited = []; _copiesVisited = []; } // Check if this object has already been visited var i, len = _visited.length; for (i = 0; i < len; i++) { // If so, get the copy we already made if (src === _visited[i]) { return _copiesVisited[i]; } } //Array if (Object.prototype.toString.call(src) == '[object Array]') { //[].slice() by itself would soft clone var ret = src.slice(); //add it to the visited array _visited.push(src); _copiesVisited.push(ret); var i = ret.length; while (i--) { ret[i] = deepCopy(ret[i], _visited, _copiesVisited); } return ret; } //If we've reached here, we have a regular object //make sure the returned object has the same prototype as the original var proto = (Object.getPrototypeOf ? Object.getPrototypeOf(src): src.__proto__); if (!proto) { proto = src.constructor.prototype; //this line would probably only be reached by very old browsers } var dest = object_create(proto); //add this object to the visited array _visited.push(src); _copiesVisited.push(dest); for (var key in src) { //Note: this does NOT preserve ES5 property attributes like 'writable', 'enumerable', etc. //For an example of how this could be modified to do so, see the singleMixin() function dest[key] = deepCopy(src[key], _visited, _copiesVisited); } return dest; } //If Object.create isn't already defined, we just do the simple shim, //without the second argument, since that all we need here var object_create = Object.create; if (typeof object_create !== 'function') { object_create = function(o) { function F() {} F.prototype = o; return new F(); }; } 
36
11 нояб. A resposta é dada por Matt Browne 11 nov. 2012-11-11 20:53 '12 at 8:53 pm 2012-11-11 20:53

O seguinte cria duas instâncias do mesmo objeto. Eu achei e usei agora. É simples e fácil de usar.

 var objToCreate = JSON.parse(JSON.stringify(cloneThis)); 
31
21 авг. Resposta dada por nathan rogers 21 de agosto 2015-08-21 18:51 '15 às 18:51 2015-08-21 18:51

Copiando objetos profundamente em javascript (eu acho o melhor e mais fácil)

1. Usando JSON.parse (JSON.stringify (object));

 var obj = { a: 1, b: { c: 2 } } var newObj = JSON.parse(JSON.stringify(obj)); obj.bc = 20; console.log(obj); // { a: 1, b: { c: 20 } } console.log(newObj); // { a: 1, b: { c: 2 } } 

2. Usando o método criado

 function cloneObject(obj) { var clone = {}; for(var i in obj) { if(obj[i] != null  typeof(obj[i])=="object") clone[i] = cloneObject(obj[i]); else clone[i] = obj[i]; } return clone; } var obj = { a: 1, b: { c: 2 } } var newObj = cloneObject(obj); obj.bc = 20; console.log(obj); // { a: 1, b: { c: 20 } } console.log(newObj); // { a: 1, b: { c: 2 } } 

3. Usando o Lo-Dash _.cloneDeep link lodash

 var obj = { a: 1, b: { c: 2 } } var newObj = _.cloneDeep(obj); obj.bc = 20; console.log(obj); // { a: 1, b: { c: 20 } } console.log(newObj); // { a: 1, b: { c: 2 } } 

4. Usando Object.assign ()

 var obj = { a: 1, b: 2 } var newObj = _.clone(obj); obj.b = 20; console.log(obj); // { a: 1, b: 20 } console.log(newObj); // { a: 1, b: 2 } 

MAS ERRADO QUANDO

 var obj = { a: 1, b: { c: 2 } } var newObj = Object.assign({}, obj); obj.bc = 20; console.log(obj); // { a: 1, b: { c: 20 } } console.log(newObj); // { a: 1, b: { c: 20 } } --> WRONG // Note: Properties on the prototype chain and non-enumerable properties cannot be copied. 

5. Usando Underscore.js _.clone link Underscore.js

 var obj = { a: 1, b: 2 } var newObj = _.clone(obj); obj.b = 20; console.log(obj); // { a: 1, b: 20 } console.log(newObj); // { a: 1, b: 2 } 

MAS ERRADO QUANDO

 var obj = { a: 1, b: { c: 2 } } var newObj = _.cloneDeep(obj); obj.bc = 20; console.log(obj); // { a: 1, b: { c: 20 } } console.log(newObj); // { a: 1, b: { c: 20 } } --> WRONG // (Create a shallow-copied clone of the provided plain object. Any nested objects or arrays will be copied by reference, not duplicated.) 

Link medium.com

Parque de Benchmarking de Desempenho do JSBEN.CH 1 ~ 3 http://jsben.ch/KVQLd 2019

08 авг. a resposta é dada TinhNQ 08 ago. 2018-08-08 11:17 '18 às 11:17 ; 2018-08-08 11:17

Lodash tem um bom método . cloneDeep (valor) :

 var objects = [{ 'a': 1 }, { 'b': 2 }]; var deep = _.cloneDeep(objects); console.log(deep[0] === objects[0]); // => false 
23
22 июня '13 в 18:03 2013-06-22 18:03 a resposta é dada por opensas em 22 de junho '13 às 18:03 2013-06-22 18:03

Crockford sugere (e eu prefiro) usar esse recurso:

 function object(o) { function F() {} F.prototype = o; return new F(); } var newObject = object(oldObject); 

É curto, funciona como esperado e você não precisa de uma biblioteca.


EDITAR:

Este é um polyfill para Object.create, então você também pode usá-lo.

 var newObject = Object.create(oldObject); 

NOTA . Se você usar alguns deles, poderá ter problemas com alguma iteração, que usa hasOwnProperty . Porque create cria um novo objeto vazio que herda oldObject . Mas ainda é útil e prático para clonar objetos.

Por exemplo, se oldObject.a = 5;

 newObject.a; // is 5 

um

 oldObject.hasOwnProperty(a); // is true newObject.hasOwnProperty(a); // is false 
23
06 окт. Resposta dada por Chris Broski Out 06 2010-10-06 18:08 '10 às 18:08 2010-10-06 18:08
 function clone(obj) { var clone = {}; clone.prototype = obj.prototype; for (property in obj) clone[property] = obj[property]; return clone; } 
22
23 сент. Resposta dada por Mark Cidade em 23 de set 2008-09-23 19:45 '08 às 7:45 pm 2008-09-23 19:45

Cópia única linha de cópia superficial ( ECMAScript 5ª edição ):

 var origin = { foo : {} }; var copy = Object.keys(origin).reduce(function(c,k){c[k]=origin[k];return c;},{}); console.log(origin, copy); console.log(origin == copy); // false console.log(origin.foo == copy.foo); // true 

Cópia única e pequena ( ECMAScript 6ª edição , 2015):

 var origin = { foo : {} }; var copy = Object.assign({}, origin); console.log(origin, copy); console.log(origin == copy); // false console.log(origin.foo == copy.foo); // true 
20
05 июля '12 в 0:44 2012-07-05 00:44 A resposta é dada por maël nison em julho 05 '12 em 12:44 2012-07-05 00:44

Просто потому, что я не видел AngularJS и думал, что люди захотят узнать...

angular.copy также предоставляет метод глубокого копирования объектов и массивов.

17
ответ дан Dan Atkinson 14 мая '16 в 1:16 2016-05-14 01:16