Pergunta

O que é a forma mais eficiente para clonar um objeto JavaScript? Eu vi obj = eval(uneval(o)); sendo usado, mas que de não-padrão e apenas suportado pelo Firefox .
eu fiz coisas como obj = JSON.parse(JSON.stringify(o)); mas causa a eficiência.
Eu também vi funções de cópia recursiva com várias falhas.
Estou surpreso não existe uma solução canônica.

Foi útil?

Solução

2019-junho de nota: Esta foi originalmente uma resposta a outra resposta, não uma resposta adequada a esta pergunta. Não faço ideia por que foi escolhido como a resposta certa. Mas desde que os upvotes bola de neve e é, de longe, o # 1 resposta a esta pergunta, ele irá resumir as soluções como uma resposta wiki.

Native profunda clonagem

É chamado de "estruturado clonagem", funciona experimentalmente em Node 11 e, mais tarde, e espero que vai pousar em navegadores. Consulte este responder para mais detalhes.

clonagem rápido com perda de dados - JSON.parse / stringify

Se você não usa Dates, funções, undefined, Infinity, regexps, Mapas, Conjuntos, gotas, filelists, ImageDatas, Arrays esparsos, datilografado matrizes ou outros tipos complexos dentro de seu objeto, um um forro muito simples de um clone de profundidade objeto é:

JSON.parse(JSON.stringify(object))

const a = {
  string: 'string',
  number: 123,
  bool: false,
  nul: null,
  date: new Date(),  // stringified
  undef: undefined,  // lost
  inf: Infinity,  // forced to 'null'
  re: /.*/,  // lost
}
console.log(a);
console.log(typeof a.date);  // Date object
const clone = JSON.parse(JSON.stringify(a));
console.log(clone);
console.log(typeof clone.date);  // result of .toISOString()

Veja o Corban resposta para benchmarks.

clonagem de confiança usando uma biblioteca

Uma vez que a clonagem de objectos não é trivial (tipos complexos, referências circulares, função, etc.), a maioria das principais bibliotecas de fornecer a função de objectos clone. Não reinventar a roda - se você já estiver usando uma biblioteca, verifique se ele tem uma função de clonagem de objeto. Por exemplo,

ES6

Para completar, nota que oferece ES6 dois mecanismos cópia superficial: Object.assign() e operador de propagação .

Outras dicas

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

Em meus testes anteriores, onde a velocidade era uma das principais preocupações achei

JSON.parse(JSON.stringify(obj))

para ser o caminho mais lento para clone profundo um objeto (que é mais lento do que jQuery.extend com a bandeira deep conjunto verdadeiro em 10-20%).

jQuery.extend é muito rápido quando a bandeira deep está definido para false (clone raso). É uma boa opção, porque inclui alguma lógica extra para validação de tipo e não copiar propriedades indefinidas, etc., mas isso também vai te atrapalhar um pouco.

Se você conhece a estrutura dos objetos que você está tentando clone ou pode evitar matrizes profundo aninhadas você pode escrever um loop for (var i in obj) simples para clonar o objeto durante a verificação hasOwnProperty e vai ser muito, muito mais rápido do que jQuery.

Por último, se você está tentando clonar uma estrutura de objeto conhecido em um loop quente você pode obter muito muito mais desempenho, simplesmente in-alinhando o procedimento clone e construir manualmente o objeto.

motores traço JavaScript chupar a otimizar loops de for..in e verificar hasOwnProperty vai ficar mais lento também. clone manual quando a velocidade é uma necessidade absoluta.

var clonedObject = {
  knownProp: obj.knownProp,
  ..
}

Cuidado usando o método JSON.parse(JSON.stringify(obj)) em objetos Date - JSON.stringify(new Date()) retorna uma representação de seqüência a data no formato ISO, que JSON.parse() não converter de volta para um objeto Date. Veja esta resposta para mais detalhes | .

Além disso, observe que, no Chrome 65, pelo menos, a clonagem nativa não é o caminho a percorrer. De acordo com a este JSPerf , realizando clonagem nativa através da criação de uma nova função é quase 800x mais lento do que usar JSON.stringify que é incrivelmente rápido todo o caminho através da placa.

Atualização para ES6

Se estiver usando Javascript ES6 tentar este método nativo para clonagem ou cópia superficial.

Object.assign({}, obj);

Assumindo que você tem apenas variáveis ??e não quaisquer funções em seu objeto, você pode apenas usar:

var newObject = JSON.parse(JSON.stringify(oldObject));

Estruturado Clonagem

O padrão HTML inclui um interno estruturado clonagem / serialização algoritmo que pode criar clones profundos de objetos. Ele ainda é limitada a certos tipos built-in, mas além dos poucos tipos suportados pelo JSON Ele também suporta datas, regexps, Mapas, Conjuntos, gotas, filelists, ImageDatas, Arrays esparsos, datilografado Arrays, e provavelmente mais no futuro . Ele também preserva referências dentro os dados clonados, permitindo-lhe apoiar as estruturas cíclicas e recursiva que poderiam causar erros de JSON.

Suporte em Node.js: Experimental ??

O módulo v8 em Node.js atualmente (a partir de Node 11) expõe a estrutura de serialização API diretamente , mas essa funcionalidade ainda está marcado como "experimental", e sujeito a alteração ou remoção de versões futuras. Se você estiver usando uma versão compatível, clonar um objeto é tão simples como:

const v8 = require('v8');

const structuredClone = obj => {
  return v8.deserialize(v8.serialize(obj));
};

Apoio Directo em Navegadores: Talvez Eventualmente? ??

Os navegadores não fornecem actualmente uma interface direta para o algoritmo de clonagem estruturado, mas uma função structuredClone() global tem sido discutido em WHATWG / html # 793 no GitHub . Como proposto atualmente, usá-lo na maioria dos casos seria tão simples como:

const clone = structuredClone(original);

A menos que este é enviado, implementações clone estruturados dos navegadores só são expostos indiretamente.

Asynchronous Solução: utilizável. ??

O caminho de menor sobrecarga para criar um clone estruturado com API existentes é a de publicar os dados através de uma porta de um MessageChannels . A outra porta vai emitir um evento message com um clone estruturado do .data anexado. Infelizmente, a escuta para esses eventos é necessariamente assíncrona, e as alternativas síncronos são menos prático.

class StructuredCloner {
  constructor() {
    this.pendingClones_ = new Map();
    this.nextKey_ = 0;

    const channel = new MessageChannel();
    this.inPort_ = channel.port1;
    this.outPort_ = channel.port2;

    this.outPort_.onmessage = ({data: {key, value}}) => {
      const resolve = this.pendingClones_.get(key);
      resolve(value);
      this.pendingClones_.delete(key);
    };
    this.outPort_.start();
  }

  cloneAsync(value) {
    return new Promise(resolve => {
      const key = this.nextKey_++;
      this.pendingClones_.set(key, resolve);
      this.inPort_.postMessage({key, value});
    });
  }
}

const structuredCloneAsync = window.structuredCloneAsync =
    StructuredCloner.prototype.cloneAsync.bind(new StructuredCloner);

Exemplo Uso:

const main = async () => {
  const original = { date: new Date(), number: Math.random() };
  original.self = original;

  const clone = await structuredCloneAsync(original);

  // They're different objects:
  console.assert(original !== clone);
  console.assert(original.date !== clone.date);

  // They're cyclical:
  console.assert(original.self === original);
  console.assert(clone.self === clone);

  // They contain equivalent values:
  console.assert(original.number === clone.number);
  console.assert(Number(original.date) === Number(clone.date));

  console.log("Assertions complete.");
};

main();

síncronos Soluções alternativas: Awful! ??

Não há boas opções para criar clones estruturados de forma síncrona. Aqui estão um par de hacks impraticáveis ??vez.

history.pushState() e history.replaceState() tanto criar um clone estruturado do seu primeiro argumento, e atribuir esse valor para history.state. Você pode usar isso para criar um clone estruturado de qualquer objeto como este:

const structuredClone = obj => {
  const oldState = history.state;
  history.replaceState(obj, null);
  const clonedObj = history.state;
  history.replaceState(oldState, null);
  return clonedObj;
};

Exemplo Uso:

'use strict';

const main = () => {
  const original = { date: new Date(), number: Math.random() };
  original.self = original;

  const clone = structuredClone(original);
  
  // They're different objects:
  console.assert(original !== clone);
  console.assert(original.date !== clone.date);

  // They're cyclical:
  console.assert(original.self === original);
  console.assert(clone.self === clone);

  // They contain equivalent values:
  console.assert(original.number === clone.number);
  console.assert(Number(original.date) === Number(clone.date));
  
  console.log("Assertions complete.");
};

const structuredClone = obj => {
  const oldState = history.state;
  history.replaceState(obj, null);
  const clonedObj = history.state;
  history.replaceState(oldState, null);
  return clonedObj;
};

main();

Embora síncrono, isso pode ser extremamente lento. Incorre toda a sobrecarga associada a manipular o histórico do navegador. Chamar esse método várias vezes pode causar Chrome para tornar-se temporariamente sem resposta.

O Notification construtor cria um clone estruturado de os seus dados associados. Ele também tenta exibir uma notificação do navegador para o usuário, mas isso vai falhar silenciosamente a menos que tenha solicitado permissão notificação. No caso de você ter a permissão para outros fins, vamos fechar imediatamente a notificação que nós criamos.

const structuredClone = obj => {
  const n = new Notification('', {data: obj, silent: true});
  n.onshow = n.close.bind(n);
  return n.data;
};

Exemplo Uso:

'use strict';

const main = () => {
  const original = { date: new Date(), number: Math.random() };
  original.self = original;

  const clone = structuredClone(original);
  
  // They're different objects:
  console.assert(original !== clone);
  console.assert(original.date !== clone.date);

  // They're cyclical:
  console.assert(original.self === original);
  console.assert(clone.self === clone);

  // They contain equivalent values:
  console.assert(original.number === clone.number);
  console.assert(Number(original.date) === Number(clone.date));
  
  console.log("Assertions complete.");
};

const structuredClone = obj => {
  const n = new Notification('', {data: obj, silent: true});
  n.close();
  return n.data;
};

main();

Se não houvesse qualquer embutido, você pode 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;
}

A maneira eficiente para clone (não deep-clone) um objeto em uma linha de código

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

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

O Object.assign () método é usado para copiar os valores de todas as propriedades próprias enumeráveis ??de um ou mais fonte de objectos para um objecto alvo.

Leia mais ...

O polyfill para apoiar os 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;
    }
  });
}

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;
}

Test:

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);

Este é o que eu estou usando:

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;
}

Copiar profundidade pelo desempenho: Classificação do melhor ao pior

  • reatribuição "=" (arranjos de cordas, matrizes número - somente)
  • Slice (matrizes de cadeia, matrizes número - apenas)
  • concatenação (matrizes de cadeia, matrizes número - apenas)
  • Custom função: for-loop ou cópia recursiva
  • $ jQuery .extend
  • JSON.parse (matrizes de cadeia, matrizes número, arranjos de objetos - apenas)
  • Underscore.js 's _.clone (arrays de cordas, matrizes numéricas - única )
  • Lo-traço _.cloneDeep

Copiar profundidade uma matriz de strings ou números (um nível - sem ponteiros de referência):

Quando uma matriz contém números e cordas - funções como .slice (), .concat (), () .splice, o operador de atribuição "=", e função do clone Underscore.js; fará uma cópia profunda dos elementos da matriz.

Onde redesignação tem o desempenho mais rápido:

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

E .slice () tem um desempenho melhor do 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

Deep copiar um array de objetos (dois ou mais níveis - ponteiros de referência):

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

Escrever uma função personalizada (tem um desempenho mais rápido 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 as funções de utilitário de terceiros:

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

Onde $ .extend tem melhor desempenho do jQuery:

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});

Há uma (chamado de “clone”) , que faz isso muito bem. Ele fornece a clonagem recursiva mais completa / cópia de objetos arbitrários que eu saiba. Ele também suporta referências circulares, o que não é coberto pelos outros respostas, ainda.

Você pode encontrá-lo no npm também. Ele pode ser usado para o navegador, bem como Node.js.

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

Instale-o com

npm install clone

or empacotá-lo com Ender .

ender build clone [...]

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

Em seguida, 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' } }

(Disclaimer:. Eu sou o autor da biblioteca)

Eu sei que este é um post antigo, mas eu pensei que este pode ser de alguma ajuda para a próxima pessoa que tropeça.

Enquanto você não atribuir um objeto a qualquer coisa que mantém nenhuma referência na memória. Então, para fazer um objeto que você deseja compartilhar entre outros objetos, você terá que criar uma fábrica assim:

var a = function(){
    return {
        father:'zacharias'
    };
},
b = a(),
c = a();
c.father = 'johndoe';
alert(b.father);

Cloning um objeto foi sempre uma preocupação em JS, mas era tudo sobre antes ES6, listo diferentes formas de copiar um objeto em JavaScript abaixo, imagine que você tem o objeto abaixo e gostaria de ter uma cópia profunda de que:

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

Existem algumas maneiras para copiar este objeto, sem alterar a origem:

1) ES5 +, usando uma função simples para fazer a cópia para você:

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 +, utilizando 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) UnderscoreJs & Loadash:

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

Hope estes ajudam ...

Se você estiver usando, o Underscore.js biblioteca tem um clone método.

var newObject = _.clone(oldObject);

Deep copiar objetos em JavaScript (eu acho que o melhor e mais simples)

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

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

2.Using 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.b.c = 20;

console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } } 

3. Usando _.cloneDeep de Lo-traço link lodash

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}

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

4. Usando Object.assign () método

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.b.c = 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.Using 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.b.c = 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.)

Referência medium.com

JSBEN.CH Desempenho de Benchmarking Parque infantil 1 ~ 3 http://jsben.ch/KVQLd Performance profunda copiar objetos em JavaScript

Aqui está uma versão da resposta de ConroyP acima que funciona mesmo se o construtor tem exigido parâmetros:

//If Object.create isn't already defined, we just do the simple shim,
//without the second argument, since that's 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;
}

Esta função também está disponível na minha simpleoo biblioteca.

Editar:

Aqui está uma versão mais robusta (graças à Justin McCandless isso agora suporta referências cíclicas também):

/**
 * Deep copy an object (make copies of all its object properties, sub-properties, etc.)
 * An improved version of http://keithdevens.com/weblog/archive/2007/Jun/07/javascript.clone
 * that doesn't break if the constructor has required parameters
 * 
 * It also borrows some code from http://stackoverflow.com/a/11621004/560114
 */ 
function deepCopy(src, /* INTERNAL */ _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's 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();
    };
}

A seguir cria duas instâncias do mesmo objeto. Eu encontrei-o e estou usando atualmente. É simples e fácil de usar.

var objToCreate = JSON.parse(JSON.stringify(cloneThis));

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

var objects = [{ 'a': 1 }, { 'b': 2 }];

var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false
function clone(obj)
 { var clone = {};
   clone.prototype = obj.prototype;
   for (property in obj) clone[property] = obj[property];
   return clone;
 }

Crockford sugere (e eu prefiro), utilizando esta função:

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

var newObject = object(oldObject);

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


EDIT:

Esta é uma polyfill para Object.create, então você também pode usar isso.

var newObject = Object.create(oldObject);

NOTA: Se você usar alguns dos isso, você pode ter problemas com alguma repetição que usam hasOwnProperty. Porque, create criar novo objeto vazio que herda oldObject. Mas ainda é útil e prático para a clonagem de objetos.

Por exemple, se oldObject.a = 5;

newObject.a; // is 5

mas:

oldObject.hasOwnProperty(a); // is true
newObject.hasOwnProperty(a); // is false

cópia superficial one-liner ( 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

E rasa copiar one-liner ( 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

Só porque eu não vi AngularJS mencionado e pensou que as pessoas podem querer saber. ..

angular.copy também fornece um método de objetos de cópia profundos e matrizes.

Não parece haver nenhum operador clone profundo ideal ainda para objetos Array-like. Como o código abaixo ilustra, voltas jQuery cloner matrizes de John Resig com propriedades não-numéricos em objectos que não são matrizes, e clonador JSON de RegDwight gotas as propriedades não-numéricos. Os testes a seguir ilustram esses pontos em vários navegadores:

function jQueryClone(obj) {
   return jQuery.extend(true, {}, obj)
}

function JSONClone(obj) {
   return JSON.parse(JSON.stringify(obj))
}

var arrayLikeObj = [[1, "a", "b"], [2, "b", "a"]];
arrayLikeObj.names = ["m", "n", "o"];
var JSONCopy = JSONClone(arrayLikeObj);
var jQueryCopy = jQueryClone(arrayLikeObj);

alert("Is arrayLikeObj an array instance?" + (arrayLikeObj instanceof Array) +
      "\nIs the jQueryClone an array instance? " + (jQueryCopy instanceof Array) +
      "\nWhat are the arrayLikeObj names? " + arrayLikeObj.names +
      "\nAnd what are the JSONClone names? " + JSONCopy.names)

Eu tenho duas respostas boas dependendo se o seu objetivo é clonar um "simples objeto JavaScript velho" ou não.

Let também supor que a sua intenção é criar um clone completo, sem referências protótipo voltar para o objeto de origem. Se você não estiver interessado em um clone completo, então você pode usar muitos dos Object.clone () rotinas fornecidas em algumas das outras respostas (padrão de Crockford).

Para planície antiga objetos JavaScript, uma boa maneira de verdade e tentou clonar um objeto em tempos de execução modernos é bastante simples:

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

Note que o objeto de origem deve ser um objeto JSON puro. Isto é, todas as suas propriedades aninhadas deve ser escalares (como boolean, string, array, objeto, etc). Quaisquer funções ou objetos especiais como RegExp ou data não será clonado.

É eficiente? Claro que sim. Nós tentamos todos os tipos de clonagem de métodos e isso funciona melhor. Tenho certeza que alguns ninja poderia evocar um método mais rápido. Mas eu suspeito que estamos falando de ganhos marginais.

Esta abordagem é apenas simples e fácil de implementar. Envolvê-la em uma função de conveniência e se você realmente precisa para espremer algum ganho, ir para mais tarde.

Agora, para objetos não-plain JavaScript, não há uma resposta muito simples. Na verdade, não pode ser por causa da natureza dinâmica das funções JavaScript e estado do objeto interior. Profunda clonagem de uma estrutura JSON com funções dentro requer que você recriar essas funções e seu contexto interno. E JavaScript simplesmente não tem uma maneira padronizada de fazer isso.

A maneira correta de fazer isso, mais uma vez, é por meio de um método de conveniência que você declarar e reutilização dentro de 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 se certificar de recriar adequadamente o gráfico dentro do novo objeto.

Estamos escrito nossa própria, mas a melhor abordagem geral que eu vi é coberto aqui:

http://davidwalsh.name/javascript-clone

Esta é a idéia certa. O autor (David Walsh) comentou a clonagem de funções generalizadas. Isso é algo que você pode escolher fazer, dependendo do seu caso de uso.

A idéia principal é que você precisa para identificador especial a instanciação de suas funções (ou classes prototypal, por assim dizer) em uma base per-tipo. Aqui, ele é fornecido alguns exemplos para RegExp e data.

Não é apenas esta breve código, mas também é muito legível. É muito fácil de estender.

Este é eficiente? Claro que sim. Dado que o objetivo é produzir um verdadeiro clone deep-cópia, então você vai ter que andar os membros do gráfico de objeto de origem. Com esta abordagem, você pode ajustar exatamente quais membros filho para tratar e como tipos personalizados punho manualmente.

Então lá vai. Duas abordagens. Ambos são eficientes na minha opinião.

Isso geralmente não é a solução mais eficiente, mas ele faz o que eu preciso. casos de teste simples abaixo ...

function clone(obj, clones) {
    // Makes a deep copy of 'obj'. Handles cyclic structures by
    // tracking cloned obj's in the 'clones' parameter. Functions 
    // are included, but not cloned. Functions members are cloned.
    var new_obj,
        already_cloned,
        t = typeof obj,
        i = 0,
        l,
        pair; 

    clones = clones || [];

    if (obj === null) {
        return obj;
    }

    if (t === "object" || t === "function") {

        // check to see if we've already cloned obj
        for (i = 0, l = clones.length; i < l; i++) {
            pair = clones[i];
            if (pair[0] === obj) {
                already_cloned = pair[1];
                break;
            }
        }

        if (already_cloned) {
            return already_cloned; 
        } else {
            if (t === "object") { // create new object
                new_obj = new obj.constructor();
            } else { // Just use functions as is
                new_obj = obj;
            }

            clones.push([obj, new_obj]); // keep track of objects we've cloned

            for (key in obj) { // clone object members
                if (obj.hasOwnProperty(key)) {
                    new_obj[key] = clone(obj[key], clones);
                }
            }
        }
    }
    return new_obj || obj;
}

cíclica teste de gama ...

a = []
a.push("b", "c", a)
aa = clone(a)
aa === a //=> false
aa[2] === a //=> false
aa[2] === a[2] //=> false
aa[2] === aa //=> true

Teste de funcionamento ...

f = new Function
f.a = a
ff = clone(f)
ff === f //=> true
ff.a === a //=> false

AngularJS

Bem, se você estiver usando angular você poderia fazer isso também

var newObject = angular.copy(oldObject);

Não concordo com a resposta com os maiores votos aqui . Um recursiva profunda Clone é muito mais rápido que o JSON.parse (JSON.stringify (obj)) abordagem mencionada.

E aqui está a função para referência rápida:

function cloneDeep (o) {
  let newO
  let i

  if (typeof o !== 'object') return o

  if (!o) return o

  if (Object.prototype.toString.apply(o) === '[object Array]') {
    newO = []
    for (i = 0; i < o.length; i += 1) {
      newO[i] = cloneDeep(o[i])
    }
    return newO
  }

  newO = {}
  for (i in o) {
    if (o.hasOwnProperty(i)) {
      newO[i] = cloneDeep(o[i])
    }
  }
  return newO
}
// obj target object, vals source object
var setVals = function (obj, vals) {
    if (obj && vals) {
        for (var x in vals) {
            if (vals.hasOwnProperty(x)) {
                if (obj[x] && typeof vals[x] === 'object') {
                    obj[x] = setVals(obj[x], vals[x]);
                } else {
                    obj[x] = vals[x];
                }
            }
        }
    }
    return obj;
};

Para as pessoas que querem usar a versão JSON.parse(JSON.stringify(obj)), mas sem perder os objetos de data, você pode usar o segundo argumento do método parse para converter as cordas voltar para a Data:

function clone(obj) {
  var regExp = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
  return JSON.parse(JSON.stringify(x), function(k, v) {
    if (typeof v === 'string' && regExp.test(v))
      return new Date(v);
    return v;
  });
}

Aqui é um método clone completo () que pode clonar qualquer objeto JavaScript. Ele lida com quase todos os casos:

function clone(src, deep) {

    var toString = Object.prototype.toString;
    if (!src && typeof src != "object") {
        // Any non-object (Boolean, String, Number), null, undefined, NaN
        return src;
    }

    // Honor native/custom clone methods
    if (src.clone && toString.call(src.clone) == "[object Function]") {
        return src.clone(deep);
    }

    // DOM elements
    if (src.nodeType && toString.call(src.cloneNode) == "[object Function]") {
        return src.cloneNode(deep);
    }

    // Date
    if (toString.call(src) == "[object Date]") {
        return new Date(src.getTime());
    }

    // RegExp
    if (toString.call(src) == "[object RegExp]") {
        return new RegExp(src);
    }

    // Function
    if (toString.call(src) == "[object Function]") {

        //Wrap in another method to make sure == is not true;
        //Note: Huge performance issue due to closures, comment this :)
        return (function(){
            src.apply(this, arguments);
        });
    }

    var ret, index;
    //Array
    if (toString.call(src) == "[object Array]") {
        //[].slice(0) would soft clone
        ret = src.slice();
        if (deep) {
            index = ret.length;
            while (index--) {
                ret[index] = clone(ret[index], true);
            }
        }
    }
    //Object
    else {
        ret = src.constructor ? new src.constructor() : {};
        for (var prop in src) {
            ret[prop] = deep
                ? clone(src[prop], true)
                : src[prop];
        }
    }
    return ret;
};
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top