Clonando um objeto no Node.js

203

Qual é a melhor maneira de clonar um objeto no node.js

por exemplo, quero evitar a situação em que:

var obj1 = {x: 5, y:5};
var obj2 = obj1;
obj2.x = 6;
console.log(obj1.x); // logs 6

O objeto pode muito bem conter tipos complexos como atributos, portanto, um simples para (var x in obj1) não seria resolvido. Preciso escrever um clone recursivo ou há algo embutido que não estou vendo?

desleixado
fonte
23
1. npm install underscore2. var _ = require('underscore')3 _.clone(objToClone).;
Salman von Abbas
4
Observe que no comentário de @ SalmanPK acima, este é um clone superficial . portanto, funcionará no exemplo de slifty, mas se houver matrizes ou objetos aninhados, eles serão referências. : /
Jesse
1
Eu encontrei este artigo muito útil: heyjavascript.com/4-creative-ways-to-clone-objects
Jordan Hudson
3
@ Jordan Hudson - Uso muito bom de JSON no segundo exemplo. var newObj = JSON.parse (JSON.stringify (oldObj)); // Agora newObj é um clone. O único problema é que o stringify não funcionará em referência recursiva, portanto, tenha cuidado.
Kfir Erez

Respostas:

298

Possibilidade 1

Cópia profunda com baixo custo:

var obj2 = JSON.parse(JSON.stringify(obj1));

Possibilidade 2 (descontinuada)

Atenção: Esta solução agora está marcada como descontinuada na documentação do Node.js :

O método util._extend () nunca foi projetado para ser usado fora dos módulos internos do Node.js. A comunidade encontrou e usou de qualquer maneira.

Ele foi descontinuado e não deve ser usado no novo código. O JavaScript é fornecido com funcionalidades internas muito semelhantes através de Object.assign ().

Resposta original ::

Para uma cópia superficial, use a função interna do Node util._extend().

var extend = require('util')._extend;

var obj1 = {x: 5, y:5};
var obj2 = extend({}, obj1);
obj2.x = 6;
console.log(obj1.x); // still logs 5

O código fonte da _extendfunção do Node está aqui: https://github.com/joyent/node/blob/master/lib/util.js

exports._extend = function(origin, add) {
  // Don't do anything if add isn't an object
  if (!add || typeof add !== 'object') return origin;

  var keys = Object.keys(add);
  var i = keys.length;
  while (i--) {
    origin[keys[i]] = add[keys[i]];
  }
  return origin;
};
jimbo
fonte
5
A pergunta pedia especificamente um clone recursivo. Este é um clone superficial.
Benjamin Atkin
28
Um nome como não _*deveria significar que é um método privado e não deve ser invocado?
Fluffy
7
Todo projeto JavaScript de qualquer tamanho possui uma ou mais implementações de extend () e o Node não é exceção. O núcleo do Node.js faz uso extensivo dessa função. Para citar Isaacs, "não vai a lugar nenhum tão cedo".
jimbo
2
funcionou perfeitamente para mim. muito melhor do que mexer com protótipo Objeto imo
Michael Dausmann
12
Esta é a resposta errada. Conforme a documentação do nó: nodejs.org/api/util.html#util_util_extend_obj O util._extend()método nunca foi projetado para ser usado fora dos módulos internos do Node.js. A comunidade encontrou e usou de qualquer maneira. Ele foi descontinuado e não deve ser usado no novo código. JavaScript vem com muito semelhante funcionalidade interna atravésObject.assign().
Jordie
265

Estou surpreso Object.assignnão ter sido mencionado.

let cloned = Object.assign({}, source);

Se disponível (por exemplo, Babel), você pode usar o operador de propagação de objetos :

let cloned = { ... source };
djanowski
fonte
1
você salvou meu dia! Graças
wzr1337
2
essa é uma solução muito melhor do que precisar importar uma biblioteca de terceiros ou usar a solução JSON imperfeita. Obrigado!
23716 Neil S
75
esta é uma cópia superficial
Jordan Davidson
14
Aviso para o Deep Clone, para o deep clone ainda é necessário usar outras alternativas. developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/…
gsalgadotoledo
1
Pelo que posso dizer, o operador de propagação de objetos não é uma coisa do ES6, mas uma proposta do estágio 3. o que significa que você pode usá-lo com babel, mas não sem o que eu entendo. github.com/tc39/…
macdja38 8/17
24
Object.defineProperty(Object.prototype, "extend", {
    enumerable: false,
    value: function(from) {
        var props = Object.getOwnPropertyNames(from);
        var dest = this;
        props.forEach(function(name) {
            if (name in dest) {
                var destination = Object.getOwnPropertyDescriptor(from, name);
                Object.defineProperty(dest, name, destination);
            }
        });
        return this;
    }
});

Isso definirá um método de extensão que você pode usar. O código vem deste artigo.

Michael Dillon
fonte
Não vejo como isso deve funcionar. Ele modifica o objeto original! Como devo usar essa função para obter um clone de um objeto? Você pode adicionar algum código de uso aqui? Depois de ler sua postagem e a postagem do blog, ainda não consigo descobrir como isso deve ser usado para clonar um objeto.
Brad
3
isso realmente funciona? "if (name in dest)" - altera apenas a propriedade se ela já existir no dest. deve ser negado.
Memical 30/03/12
8
Modificar Object.prototype não deveria ser verboten? Além disso, o link do artigo está quebrado.
Daniel Schaffer
Apenas tentei o link do artigo e funciona para mim. Talvez tenha sido um problema de rede quando você tentou.
9788 Michael Dillon
Com base em vários comentários, atualizei a resposta para incluir uma variante que não é adicionada ao protótipo do objeto.
Shamasis Bhattacharya
20
var obj2 = JSON.parse(JSON.stringify(obj1));
user2516109
fonte
2
Isso já foi sugerido nesta resposta existente, não adianta repeti-la.
Shadow Wizard é Ear For You
@ ShadowWizard estes são métodos diferentes. Este simplesmente convertidos para JSON e de volta ao objeto, enquanto ligada usos resposta Object.keys()para percorrer objeto
mente
Esta resposta está errada. O JSON.stringify pega um objeto de data e o reduz a uma sequência e, depois de analisá-lo, deixa-o como uma sequência para que modifique o estado do seu objeto, deixando-o com um objeto diferente do inicialmente em caso de datas.
twboc 17/01
8

Existem alguns módulos de nó por aí, se você não quiser "rodar sozinho". Este parece ser bom: https://www.npmjs.com/package/clone

Parece que ele lida com todos os tipos de coisas, incluindo referências circulares. Na página do github :

mestres de clones clonando objetos, matrizes, objetos Data e objetos RegEx. Tudo é clonado recursivamente, para que você possa clonar datas em matrizes em objetos, por exemplo. [...] referências circulares? Sim!

Clint Harris
fonte
7

Esse código também é uma causa de trabalho. O método Object.create () cria um novo objeto com o objeto e as propriedades do protótipo especificado.

var obj1 = {x:5, y:5};

var obj2 = Object.create(obj1);

obj2.x; //5
obj2.x = 6;
obj2.x; //6

obj1.x; //5
Hiron
fonte
4
esta é uma cópia superficial
Radagast the Brown
6

A maneira mais simples e rápida de clonar um objeto no NodeJS é usar o método Object.keys (obj)

var a = {"a": "a11", "b": "avc"};
var b;

for(var keys = Object.keys(a), l = keys.length; l; --l)
{
   b[ keys[l-1] ] = a[ keys[l-1] ];
}
b.a = 0;

console.log("a: " + JSON.stringify(a)); // LOG: a: {"a":"a11","b":"avc"} 
console.log("b: " + JSON.stringify(b)); // LOG: b: {"a":0,"b":"avc"}

O método Object.keys requer JavaScript 1.8.5; O nodeJS v0.4.11 suporta este método

mas é claro que para objetos aninhados precisam implementar funções recursivas


Outra solução é usar JSON nativo (implementado no JavaScript 1.7), mas é muito mais lento (~ 10 vezes mais lento) que o anterior

var a = {"a": i, "b": i*i};
var b = JSON.parse(JSON.stringify(a));
b.a = 0;
nihil
fonte
5

Há também um projeto no Github que visa ser uma porta mais direta do jQuery.extend():

https://github.com/dreamerslab/node.extend

Um exemplo, modificado a partir dos documentos do jQuery :

var extend = require('node.extend');

var object1 = {
    apple: 0,
    banana: {
        weight: 52,
        price: 100
    },
    cherry: 97
};

var object2 = {
    banana: {
        price: 200
    },
    durian: 100
};

var merged = extend(object1, object2);
Randy
fonte
4

Vocês estão sofrendo e a solução é simples.

var obj1 = {x: 5, y:5};

var obj2 = {...obj1}; // Estrondo

Ngend Lio
fonte
3

Procurando por uma verdadeira opção de clone, deparei-me com o link do ridcully aqui:

http://my.opera.com/GreyWyvern/blog/show.dml/1725165

Modifiquei a solução nessa página para que a função anexada ao Objectprotótipo não seja enumerável. Aqui está o meu resultado:

Object.defineProperty(Object.prototype, 'clone', {
    enumerable: false,
    value: function() {
        var newObj = (this instanceof Array) ? [] : {};
        for (i in this) {
        if (i == 'clone') continue;
            if (this[i] && typeof this[i] == "object") {
                newObj[i] = this[i].clone();
            } else newObj[i] = this[i]
        } return newObj;
    }
});

Espero que isso ajude outra pessoa também. Observe que existem algumas advertências ... particularmente com propriedades denominadas "clone". Isto funciona bem para mim. Não aceito nenhum crédito por escrevê-lo. Mais uma vez, mudei apenas como estava sendo definido.

Brad
fonte
Isto está errado. O tipo de datas é um objeto, portanto esse código substitui as datas por objetos vazios ... Não use isso.
Jtblin
0

Se você estiver usando script de café, é tão fácil quanto:

newObject = {}
newObject[key] = value  for own key,value of oldObject

Embora este não seja um clone profundo.

Balupton
fonte
0

Nenhuma das respostas me satisfez, várias não funcionam ou são apenas clones superficiais, as respostas do @ clint-harris e do uso do JSON.parse / stringify são boas, mas bem lentas. Encontrei um módulo que faz clonagem profunda rápida: https://github.com/AlexeyKupershtokh/node-v8-clone

jtblin
fonte
0

Não há uma maneira integrada de fazer um clone real (cópia detalhada) de um objeto no node.js. Existem alguns casos complicados, então você definitivamente deve usar uma biblioteca para isso. Eu escrevi essa função para minha biblioteca simpleoo . Você pode usar odeepCopy função sem usar mais nada da biblioteca (que é bem pequena) se não precisar. Essa função suporta a clonagem de vários tipos de dados, incluindo matrizes, datas e expressões regulares, suporta referências recursivas e também trabalha com objetos cujas funções construtoras possuem parâmetros necessários.

Aqui está o código:

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

/**
 * 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 = function deepCopy(src, /* INTERNAL */ _visited) {
    if(src == null || typeof(src) !== 'object'){
        return src;
    }

    // Initialize the visited objects array if needed
    // This is used to detect cyclic references
    if (_visited == undefined){
        _visited = [];
    }
    // Ensure src has not already been visited
    else {
        var i, len = _visited.length;
        for (i = 0; i < len; i++) {
            // If src was already visited, don't try to copy it, just return the reference
            if (src === _visited[i]) {
                return src;
            }
        }
    }

    // Add this object to the visited array
    _visited.push(src);

    //Honor native/custom clone methods
    if(typeof src.clone == 'function'){
        return src.clone(true);
    }

    //Special cases:
    //Array
    if (Object.prototype.toString.call(src) == '[object Array]') {
        //[].slice(0) would soft clone
        ret = src.slice();
        var i = ret.length;
        while (i--){
            ret[i] = deepCopy(ret[i], _visited);
        }
        return ret;
    }
    //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);
    }

    //If we've reached here, we have a regular object, array, or function

    //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 ret = object_create(proto);

    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
        ret[key] = deepCopy(src[key], _visited);
    }
    return ret;
};
Matt Browne
fonte
0
npm install node-v8-clone

Clonador mais rápido, ele abre o método clone nativo do node.js

var clone = require('node-v8-clone').clone;
var newObj = clone(obj, true); //true - deep recursive clone
Oleksiy Chechel
fonte
0

Outra solução é encapsular diretamente na nova variável usando: obj1= {...obj2}

labelle
fonte
Esta é uma cópia superficial
Rémi Doolaeghe 30/09/19
0

Você também pode usar esta biblioteca de clones para clonar objetos profundamente.

 npm install --save clone
const clone = require('clone');

const clonedObject = clone(sourceObject);
Lanil Marasinghe
fonte
-2

Você pode criar um protótipo de objeto e, em seguida, chamar a instância do objeto sempre que desejar usar e alterar o objeto:

function object () {
  this.x = 5;
  this.y = 5;
}
var obj1 = new object();
var obj2 = new object();
obj2.x = 6;
console.log(obj1.x); //logs 5

Você também pode passar argumentos para o construtor de objetos

function object (x, y) {
   this.x = x;
   this.y = y;
}
var obj1 = new object(5, 5);
var obj2 = new object(6, 6);
console.log(obj1.x); //logs 5
console.log(obj2.x); //logs 6

Espero que isso seja útil.

user3459287
fonte