Clonage d'un objet dans Node.js


203

Quelle est la meilleure façon 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 bien contenir des types complexes en tant qu'attributs, donc un simple for (var x dans obj1) ne résoudra pas. Dois-je écrire moi-même un clone récursif ou y a-t-il quelque chose que je ne vois pas?


23
1. npm install underscore2. var _ = require('underscore')3 _.clone(objToClone).;
Salman von Abbas

4
Notez que dans le commentaire de @ SalmanPK ci-dessus, il s'agit d'un clone peu profond . cela fonctionnera donc pour l'exemple de slifty, mais s'il y a des tableaux ou des objets imbriqués, ils seront des références. : /
Jesse

1
J'ai trouvé cet article très utile: heyjavascript.com/4-creative-ways-to-clone-objects
Jordan Hudson

3
@Jordan Hudson - Très belle utilisation de JSON dans le deuxième exemple. var newObj = JSON.parse (JSON.stringify (oldObj)); // Maintenant, newObj est un clone. Le seul problème est que stringify ne fonctionnera pas sur les références récursives, il faut donc être prudent.
Kfir Erez

Réponses:


298

Possibilité 1

Copie profonde sans fioritures:

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

Possibilité 2 (obsolète)

Attention: Cette solution est désormais marquée comme déconseillée dans la documentation de Node.js :

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

Il est obsolète et ne doit pas être utilisé dans le nouveau code. JavaScript est livré avec des fonctionnalités intégrées très similaires via Object.assign ().

Réponse originale ::

Pour une copie superficielle, utilisez la fonction intégrée de 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

Le code source de la _extendfonction de Node 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;
};

5
La question appelait spécifiquement un clone récursif. Ceci est un clone peu profond.
Benjamin Atkin

28
N'est-ce pas un nom comme _*censé signifier que c'est une méthode privée et ne doit pas être invoqué?
Fluffy

7
Chaque projet JavaScript de n'importe quelle taille a une ou plusieurs implémentations de extend (), et Node ne fait pas exception. Le noyau Node.js utilise largement cette fonction. Pour citer Isaacs, "ça ne va nulle part de sitôt."
jimbo

2
a parfaitement fonctionné pour moi. bien mieux que de jouer avec le prototype d'objet imo
Michael Dausmann

12
Ceci est la mauvaise réponse. Selon la documentation du nœud: nodejs.org/api/util.html#util_util_extend_obj La util._extend()méthode n'a jamais été conçue pour être utilisée en dehors des modules Node.js internes. La communauté l'a quand même trouvée et utilisée. Il est obsolète et ne doit pas être utilisé dans le nouveau code. JavaScript est livré avec des fonctionnalités intégrées très similaires viaObject.assign().
Jordie

265

Je suis surpris que cela Object.assignn'ait pas été mentionné.

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

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

let cloned = { ... source };

1
tu m'as sauvé la journée! Merci
wzr1337

2
il s'agit d'une bien meilleure solution que d'avoir à importer une bibliothèque tierce ou à utiliser la solution de contournement JSON imparfaite. Merci!
Neil S

75
Ceci est une copie superficielle
Jordan Davidson

14
Avertissement pour Deep Clone, Pour Deep Clone, vous devez toujours utiliser d'autres alternatives. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
gsalgadotoledo

1
D'après ce que je peux dire, l'opérateur de propagation d'objet n'est pas une chose ES6 mais plutôt une proposition de l'étape 3. ce qui signifie que vous pouvez l'utiliser avec babel mais pas sans ce que je comprends. github.com/tc39/…
macdja38

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

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


Je ne vois pas comment cela est censé fonctionner. Il modifie l'objet d'origine! Comment suis-je censé utiliser cette fonction pour obtenir un clone d'un objet? Pouvez-vous ajouter un code d'utilisation ici? Après avoir lu votre article et le blog, je n'arrive toujours pas à comprendre comment cela est destiné à être utilisé pour cloner un objet.
Brad

3
ça marche vraiment? "if (nom dans dest)" - ne changera la propriété que si elle existe déjà dans dest. il faut l'annuler.
memical

8
La modification de Object.prototype n'est-elle pas censée être verboten? De plus, ce lien d'article est rompu.
Daniel Schaffer

Je viens d'essayer le lien de l'article et cela fonctionne pour moi. Peut-être que c'était un blip réseau lorsque vous l'avez essayé.
Michael Dillon

Sur la base d'un certain nombre de commentaires, j'ai mis à jour la réponse pour inclure une variante qui ne s'ajoute pas au prototype de l'objet.
Shamasis Bhattacharya

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

2
Cela a déjà été suggéré dans cette réponse existante, inutile de le répéter.
Shadow Wizard est Ear For You

@ShadowWizard ce sont différentes méthodes. Celui-ci se convertit simplement en json et retourne à l'objet, tandis que la réponse liée utilise Object.keys()pour itérer à travers l'objet
mente

Cette réponse est fausse. JSON.stringify prend un objet date et le réduit à une chaîne, puis après l'avoir analysé, il le laisse sous forme de chaîne afin qu'il mute l'état de votre objet, vous laissant avec un objet différent de celui initialement dans le cas des dates.
twboc



8

Il y a des modules Node là-bas si vous ne voulez pas "rouler 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. Depuis la page github :

clone maîtrise les objets de clonage, les tableaux, les objets Date et les objets RegEx. Tout est cloné récursivement, de sorte que vous pouvez cloner des dates dans des tableaux d'objets, par exemple. [...] Références circulaires? Oui!


7

Ce code est également un travail car la méthode Object.create () crée un nouvel objet avec l'objet prototype et les propriétés 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

4
ceci est une copie superficielle
Radagast the Brown

6

Le moyen le plus simple et le plus rapide pour cloner un objet dans NodeJS est d'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 nécessite JavaScript 1.8.5; nodeJS v0.4.11 prend en charge cette méthode

mais bien sûr, pour les objets imbriqués, il faut implémenter des fonctions


Une autre solution consiste à utiliser le JSON natif (implémenté dans JavaScript 1.7), mais il est beaucoup plus lent (~ 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;

5

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

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

Un exemple, modifié à partir des documents 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);


4

Vous souffrez tous et pourtant la solution est simple.

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

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


3

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

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

J'ai modifié la solution sur cette page afin que la fonction attachée au Objectprototype 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 mises en garde ... en particulier 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.


C'est faux. Le type de dates est un objet, donc ce code remplacerait les dates par des objets vides ... Ne l'utilisez pas.
jtblin


0

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

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 fait du clonage en profondeur rapidement: https://github.com/AlexeyKupershtokh/node-v8-clone


0

Il n'y a aucun moyen intégré de faire un vrai clone (copie complète) d'un objet dans node.js. Il y a quelques cas délicats, vous devriez donc certainement utiliser une bibliothèque pour cela. J'ai écrit une telle fonction pour ma bibliothèque simpleoo . Vous pouvez utiliser ledeepCopy fonction sans utiliser quoi que ce soit d'autre de la bibliothèque (qui est assez petite) 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 en charge les références récursives et elle fonctionne également avec les objets dont les fonctions 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
npm install node-v8-clone

Cloneur le plus rapide, il ouvre la méthode de clonage natif de node.js

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

0

Une autre solution consiste à encapsuler directement dans la nouvelle variable en utilisant: obj1= {...obj2}


Ceci est une copie superficielle
Rémi Doolaeghe

0

Vous pouvez également utiliser cette bibliothèque de clones pour cloner des objets en profondeur.

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

const clonedObject = clone(sourceObject);

-2

Vous pouvez créer un prototype d'objet, puis appeler une instance d'objet chaque fois que vous souhaitez utiliser et modifier un objet:

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

Vous pouvez également passer des arguments au constructeur d'objet

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

J'espère que cela vous sera utile.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.