web-dev-qa-db-fra.com

Cloner un objet dans Node.js

Quel est le meilleur moyen de cloner un objet dans node.js

par exemple. Je veux éviter la situation où:

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

L'objet peut très bien contenir des types complexes en tant qu'attributs, donc un simple for (var x dans obj1) ne résoudrait pas. Dois-je écrire moi-même un clone récursif ou y a-t-il quelque chose d'intégré que je ne vois pas?

181
slifty

Possibilité 1

Copie profonde à faible fioriture:

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

Possibilité 2 (obsolète)

Attention: Cette solution est maintenant marquée comme obsolète dans le documentation de Node.js :

La méthode util._extend () n'a jamais été conçue pour être utilisée en dehors des modules internes de Node.js. La communauté a trouvé et utilisé de toute façon.

Il est obsolète et ne devrait pas être utilisé dans le nouveau code. JavaScript est livré avec une fonctionnalité intégrée très similaire via Object.assign ().

Réponse originale: :

Pour une copie peu profonde, utilisez la fonction intégrée util._extend() du nœud.

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

Le code source de la fonction _extend du noeud est ici: 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;
};
283
jimbo

Je suis surpris que Object.assign n'ait pas été mentionné.

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

Si disponible (par exemple Babel), vous pouvez utiliser le opérateur de propagation d'objet :

let cloned = { ... source };
230
djanowski
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;
    }
});

Cela définira une méthode d'extension que vous pouvez utiliser. Le code vient de cet article.

23
Michael Dillon
var obj2 = JSON.parse(JSON.stringify(obj1));
18
user2516109

Vous pouvez utiliser la fonction extend de JQuery:

var newClone= jQuery.extend({}, oldObject);  
var deepClone = jQuery.extend(true, {}, oldObject); 

Il existe aussi un plugin Node.js:

https://github.com/shimondoodkin/nodejs-clone-extend

Pour le faire sans JQuery ou Plugin, lisez ceci ici:

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

13
ridcully

Départ nderscore.js . Il a à la fois clone et extend et de nombreuses autres fonctions très utiles.

Cela peut être utile: tilisation du module Underscore avec Node.js

12
esp

Il existe quelques Node modules si vous ne voulez pas "lancer le vôtre". Celui-ci a l'air bien: https://www.npmjs.com/package/clone

On dirait qu'il gère toutes sortes de choses, y compris les références circulaires. De la page github :

cloner les objets de clonage, les tableaux, les objets Date et les objets RegEx. Tout est cloné de manière récursive, afin que vous puissiez cloner des dates dans des tableaux d'objets, par exemple. [...] Références circulaires? Oui!

8
Clint Harris

Ce code est également une cause de travail. La méthode Object.create () crée un nouvel objet avec les propriétés et l’objet prototype spécifiés.

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

var obj2 = Object.create(obj1);

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

obj1.x; //5
6
Hiron

Le moyen le plus simple et le plus rapide de cloner un objet dans NodeJS consiste à utiliser la méthode 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"}

La méthode Object.keys requiert JavaScript 1.8.5; nodeJS v0.4.11 supporte cette méthode

mais bien sûr, les objets imbriqués doivent implémenter une fonction récursive


Une autre solution consiste à utiliser le JSON natif (implémenté dans JavaScript 1.7), mais il est beaucoup plus lent (environ 10 fois plus lent) que le précédent.

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

Il existe également un projet sur Github qui vise à être un port plus direct de la jQuery.extend():

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

Un exemple, modifié à partir de docs 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);
5
Randy

Il existe une autre bibliothèque lodash , elle a clone et cloneDeep , ainsi que de nombreuses autres fonctions utiles.

4
zangw

À la recherche d'une véritable option de clonage, je suis tombé sur le lien ridicule de cet outil:

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

J'ai modifié la solution sur cette page afin que la fonction attachée au prototype Object ne soit pas énumérable. Voici mon résultat:

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

Espérons que cela aide aussi quelqu'un d'autre. Notez qu'il y a quelques réserves ... particulièrement avec les propriétés nommées "clone". Cela fonctionne bien pour moi. Je ne prends aucun crédit pour l'écrire. Encore une fois, j'ai seulement changé la façon dont il était défini.

3
Brad

Vous pouvez également utiliser SugarJS dans NodeJS.

http://sugarjs.com/

Ils ont une fonctionnalité de clone très propre: http://sugarjs.com/api/Object/clone

1
Pepijn

Aucune des réponses ne m'a satisfait, plusieurs ne fonctionnent pas ou ne sont que des clones superficiels. Les réponses de @ clint-harris et l'utilisation de JSON.parse/stringify sont bonnes mais assez lentes. J'ai trouvé un module qui effectue un clonage profond rapide: https://github.com/AlexeyKupershtokh/node-v8-clone

0
jtblin

Il n'y a pas de méthode intégrée pour créer un véritable clone (copie complète) d'un objet dans node.js. Il y a quelques cas délicats Edge, vous devriez donc absolument utiliser une bibliothèque pour cela. J'ai écrit une telle fonction pour ma bibliothèque simpleoo . Vous pouvez utiliser la fonction deepCopy sans utiliser d'autre élément de la bibliothèque (ce qui est assez petit) si vous n'en avez pas besoin. Cette fonction prend en charge le clonage de plusieurs types de données, y compris les tableaux, les dates et les expressions régulières. Elle prend également en charge les références récursives. Elle fonctionne également avec les objets dont les fonctions de constructeur ont des paramètres requis.

Voici le code:

//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;
};
0
Matt Browne
npm install node-v8-clone

Le cloner le plus rapide, il ouvre la méthode de clonage native de node.js

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

Si vous utilisez coffee-script, c'est aussi simple que:

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

Bien que ce ne soit pas un clone profond.

0
balupton