216 votes

Cloner un objet sans référence javascript

J'ai un gros objet avec beaucoup de données. Et je veux le cloner dans une autre variable. Quand je règle certains paramètres de l'instance, B a le même résultat dans l'objet original :

var obj = {a: 25, b: 50, c: 75};
var A = obj;
var B = obj;

A.a = 30;
B.a = 40;

alert(obj.a + " " + A.a + " " + B.a); // 40 40 40

Mon rendement devrait être de 25 30 40. Avez-vous une idée ?

EDIT

Merci à tous. J'ai changé le code de dystroy et voici mon résultat :

Object.prototype.clone = Array.prototype.clone = function()
{
    if (Object.prototype.toString.call(this) === '[object Array]')
    {
        var clone = [];
        for (var i=0; i<this.length; i++)
            clone[i] = this[i].clone();

        return clone;
    } 
    else if (typeof(this)=="object")
    {
        var clone = {};
        for (var prop in this)
            if (this.hasOwnProperty(prop))
                clone[prop] = this[prop].clone();

        return clone;
    }
    else
        return this;
}

var obj = {a: 25, b: 50, c: 75};
var A = obj.clone();
var B = obj.clone();
A.a = 30;
B.a = 40;
alert(obj.a + " " + A.a + " " + B.a);

var arr = [25, 50, 75];
var C = arr.clone();
var D = arr.clone();
C[0] = 30;
D[0] = 40;
alert(arr[0] + " " + C[0] + " " + D[0]);

243voto

Armel Larcier Points 5391

Si vous utilisez un = pour attribuer une valeur à un var avec un objet sur le côté droit, javascript ne copiera pas mais fera référence à l'objet.

Spoiler : utiliser JSON.parse(JSON.stringify(obj)) peut fonctionner mais est coûteux, et pourrait jeter un TypeError dans le cas de

const a = {};
const b = { a };
a.b = b;
const clone = JSON.parse(JSON.stringify(a));
/* Throws
Uncaught TypeError: Converting circular structure to JSON
    --> starting at object with constructor 'Object'
    |     property 'b' -> object with constructor 'Object'
    --- property 'a' closes the circle
    at JSON.stringify (<anonymous>)
    at <anonymous>:4:6
*/

Depuis es2015, si vous voulez une copie superficielle (cloner l'objet, mais en conservant des références profondes dans la structure interne), vous pouvez utiliser la déstructuration :

const obj = { foo: { bar: "baz" } };
const shallowClone = { ...obj };

shallowClone est un nouvel objet, mais shallowClone.foo contient une référence au même objet que obj.foo .

Vous pouvez utiliser lodash 's clone qui fait la même chose, si vous n'avez pas accès à l'opérateur d'étalement.

var obj = {a: 25, b: 50, c: 75};
var A = _.clone(obj);

Ou lodash 's cloneDeep si votre objet a plusieurs niveaux d'objet

var obj = {a: 25, b: {a: 1, b: 2}, c: 75};
var A = _.cloneDeep(obj);

Ou lodash 's merge si vous voulez étendre l'objet source

var obj = {a: 25, b: {a: 1, b: 2}, c: 75};
var A = _.merge({}, obj, {newkey: "newvalue"});

Ou vous pouvez utiliser jQuerys extend méthode :

var obj = {a: 25, b: 50, c: 75};
var A = $.extend(true,{},obj);

Voici jQuery 1.11 code source de la méthode extend :

jQuery.extend = jQuery.fn.extend = function() {
    var src, copyIsArray, copy, name, options, clone,
        target = arguments[0] || {},
        i = 1,
        length = arguments.length,
        deep = false;

    // Handle a deep copy situation
    if ( typeof target === "boolean" ) {
        deep = target;

        // skip the boolean and the target
        target = arguments[ i ] || {};
        i++;
    }

    // Handle case when target is a string or something (possible in deep copy)
    if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
        target = {};
    }

    // extend jQuery itself if only one argument is passed
    if ( i === length ) {
        target = this;
        i--;
    }

    for ( ; i < length; i++ ) {
        // Only deal with non-null/undefined values
        if ( (options = arguments[ i ]) != null ) {
            // Extend the base object
            for ( name in options ) {
                src = target[ name ];
                copy = options[ name ];

                // Prevent never-ending loop
                if ( target === copy ) {
                    continue;
                }

                // Recurse if we're merging plain objects or arrays
                if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
                    if ( copyIsArray ) {
                        copyIsArray = false;
                        clone = src && jQuery.isArray(src) ? src : [];

                    } else {
                        clone = src && jQuery.isPlainObject(src) ? src : {};
                    }

                    // Never move original objects, clone them
                    target[ name ] = jQuery.extend( deep, clone, copy );

                // Don't bring in undefined values
                } else if ( copy !== undefined ) {
                    target[ name ] = copy;
                }
            }
        }
    }

    // Return the modified object
    return target;
};

var item ={ 'a': 1, 'b': 2}
Object.assign({}, item);

132voto

I Hate Lazy Points 18168

Bien qu'il ne s'agisse pas d'un clonage, une façon simple d'obtenir votre résultat est d'utiliser l'objet original comme prototype d'un nouvel objet.

Vous pouvez le faire en utilisant Object.create :

var obj = {a: 25, b: 50, c: 75};
var A = Object.create(obj);
var B = Object.create(obj);

A.a = 30;
B.a = 40;

alert(obj.a + " " + A.a + " " + B.a); // 25 30 40

Cela crée un nouvel objet dans A y B que hérite de de obj . Cela signifie que vous pouvez ajouter des propriétés sans affecter l'original.

Pour prendre en charge les anciennes implémentations, vous pouvez créer un fichier de type (partiel) cale qui fonctionnera pour cette simple tâche.

if (!Object.create)
    Object.create = function(proto) {
        function F(){}
        F.prototype = proto;
        return new F;
    }

Il n'émule pas toutes les fonctionnalités de l'application Object.create mais il répondra à vos besoins ici.

17voto

dystroy Points 145126

Vous pourriez définir une fonction de clonage.

J'utilise celui-ci :

function goclone(source) {
    if (Object.prototype.toString.call(source) === '[object Array]') {
        var clone = [];
        for (var i=0; i<source.length; i++) {
            clone[i] = goclone(source[i]);
        }
        return clone;
    } else if (typeof(source)=="object") {
        var clone = {};
        for (var prop in source) {
            if (source.hasOwnProperty(prop)) {
                clone[prop] = goclone(source[prop]);
            }
        }
        return clone;
    } else {
        return source;
    }
}

var B = goclone(A);

Il ne copie pas le prototype, les fonctions, etc. Mais vous devez l'adapter (et peut-être le simplifier) pour vos propres besoins.

10voto

RobG Points 41170

A y B font référence au même objet, donc A.a y B.a font référence à la même propriété du même objet.

Editar

Voici une fonction de "copie" qui peut faire l'affaire, elle peut faire des clones superficiels et profonds. Notez les avertissements. Elle copie toutes les propriétés énumérables d'un objet (pas les propriétés héritées), y compris celles qui ont de fausses valeurs (je ne comprends pas pourquoi les autres approches les ignorent), elle ne copie pas non plus les propriétés inexistantes des tableaux épars.

Il n'existe pas de fonction générale de copie ou de clonage, car il existe de nombreuses idées différentes sur ce qu'une copie ou un clone doit faire dans chaque cas. La plupart excluent les objets hôtes, ou tout ce qui n'est pas des objets ou des tableaux. Celle-ci copie également les primitives. Que devrait-il se passer avec les fonctions ?

Jetez donc un coup d'œil à ce qui suit, c'est une approche légèrement différente des autres.

/* Only works for native objects, host objects are not
** included. Copies Objects, Arrays, Functions and primitives.
** Any other type of object (Number, String, etc.) will likely give 
** unexpected results, e.g. copy(new Number(5)) ==> 0 since the value
** is stored in a non-enumerable property.
**
** Expects that objects have a properly set *constructor* property.
*/
function copy(source, deep) {
   var o, prop, type;

  if (typeof source != 'object' || source === null) {
    // What do to with functions, throw an error?
    o = source;
    return o;
  }

  o = new source.constructor();

  for (prop in source) {

    if (source.hasOwnProperty(prop)) {
      type = typeof source[prop];

      if (deep && type == 'object' && source[prop] !== null) {
        o[prop] = copy(source[prop]);

      } else {
        o[prop] = source[prop];
      }
    }
  }
  return o;
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X