web-dev-qa-db-fra.com

Cloner un objet sans référence javascript

J'ai un gros objet avec beaucoup de données. Et je veux cloner ceci dans une autre variable. Quand je définis un paramètre de l'instance B a le même résultat dans l'objet d'origine:

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

Ma sortie devrait être 25 30 40. Des idées?

EDIT

Merci tout le monde. Je change 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]);
133
EnZo

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

Vous pouvez utiliser la méthode clone de lodash

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

Ou la méthode cloneDeep de lodash si votre objet a plusieurs niveaux d'objet

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

Ou la méthode merge de lodash 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 la méthode jQuerys extend:

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

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

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;
};
150
Armel Larcier

Bien que cela ne soit pas un clonage, un moyen simple d'obtenir votre résultat consiste à utiliser l'objet d'origine 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 et B qui hérite de obj. Cela signifie que vous pouvez ajouter des propriétés sans affecter l'original.

Pour prendre en charge les implémentations traditionnelles, vous pouvez créer un shim (partiel) qui fonctionnera pour cette tâche simple.

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

Il n'émule pas toutes les fonctionnalités de Object.create, mais il convient à vos besoins ici.

84
I Hate Lazy

Vous pouvez 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 devriez l'adapter (et peut-être le simplifier) ​​à vos propres besoins.

12
Denys Séguret

A et B référencent le même objet. A.a et B.a référencent donc la même propriété du même objet.

Modifier

Voici une fonction "copie" qui peut faire le travail, il peut faire des clones superficiels et profonds. Notez les mises en garde. Il copie toutes les propriétés énumérables d'un objet (propriétés non héritées), y compris celles avec des valeurs de Falsey (je ne comprends pas pourquoi les autres approches les ignorent), il 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 que doit faire une copie ou un clone dans tous les cas. La plupart excluent les objets Host, ou autre chose que Objects ou Arrays. Celui-ci copie également les primitives. Que devrait-il arriver avec les fonctions?

Regardez donc 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;
}
7
RobG