120 votes

Copier la valeur d'une variable dans une autre

J'ai une variable qui a pour valeur un objet JSON. J'assigne directement cette variable à une autre variable afin qu'elles partagent la même valeur. Voici comment cela fonctionne :

var a = $('#some_hidden_var').val(),
    b = a;

Cela fonctionne et les deux ont la même valeur. J'utilise un mousemove gestionnaire d'événement pour mettre à jour b dans toute mon application. Lors d'un clic sur un bouton, je veux revenir en arrière. b à la valeur originale, c'est-à-dire la valeur stockée dans a .

$('#revert').on('click', function(e){
    b = a;
});

Après cela, si j'utilise le même mousemove le gestionnaire d'événement, il met à jour les deux a y b alors qu'auparavant il ne mettait à jour que b comme prévu.

Je suis perplexe face à ce problème ! Quel est le problème ?

224voto

Michael Geary Points 10391

Il est important de comprendre ce que le = en JavaScript fait et ne fait pas.

El = ne fait pas un copie des données.

El = L'opérateur crée un nouveau référence à la même données.

Après avoir exécuté votre code original :

var a = $('#some_hidden_var').val(),
    b = a;

a y b sont maintenant deux noms différents pour le même objet .

Toute modification apportée au contenu de cet objet sera perçue de manière identique, que vous y fassiez référence par l'intermédiaire de l'objet a ou la variable b variable. Ils sont le même objet.

Donc, quand vous essayez plus tard de "revenir" b à l'original a avec ce code :

b = a;

Le code fait en réalité rien du tout parce que a y b sont exactement la même chose. Le code est le même que si vous aviez écrit :

b = b;

ce qui évidemment ne fera rien.

Pourquoi votre nouveau code fonctionne-t-il ?

b = { key1: a.key1, key2: a.key2 };

Ici, vous créez un tout nouvel objet avec l'attribut {...} objet littéral. Ce nouvel objet n'est pas le même que votre ancien objet. Vous définissez donc maintenant b comme une référence à ce nouvel objet, ce qui fait ce que vous voulez.

Pour gérer n'importe quel objet arbitraire, vous pouvez utiliser une fonction de clonage d'objet telle que celle indiquée dans la réponse d'Armand, ou, puisque vous utilisez jQuery, utilisez simplement la fonction $.extend() fonction . Cette fonction va faire une copie superficielle ou une copie profonde d'un objet. (Ne confondez pas cette fonction avec la fonction $().clone() méthode qui sert à copier des éléments du DOM, pas des objets).

Pour une copie superficielle :

b = $.extend( {}, a );

Ou une copie profonde :

b = $.extend( true, {}, a );

Quelle est la différence entre une copie superficielle et une copie profonde ? Une copie superficielle est similaire à votre code qui crée un nouvel objet avec un objet littéral. Elle crée un nouvel objet de haut niveau contenant des références aux mêmes propriétés que l'objet original.

Si votre objet ne contient que des types primitifs comme des nombres et des chaînes de caractères, une copie profonde et une copie superficielle feront exactement la même chose. Mais si votre objet contient d'autres objets ou des tableaux imbriqués à l'intérieur de lui, alors une copie superficielle n'est pas nécessaire. copie ces objets imbriqués, il crée simplement des références à ceux-ci. On peut donc avoir le même problème avec les objets imbriqués qu'avec l'objet de niveau supérieur. Par exemple, avec cet objet :

var obj = {
    w: 123,
    x: {
        y: 456,
        z: 789
    }
};

Si vous faites une copie superficielle de cet objet, alors l'objet x de votre nouvel objet est la même x de l'original :

var copy = $.extend( {}, obj );
copy.w = 321;
copy.x.y = 654;

Maintenant, vos objets vont ressembler à ceci :

// copy looks as expected
var copy = {
    w: 321,
    x: {
        y: 654,
        z: 789
    }
};

// But changing copy.x.y also changed obj.x.y!
var obj = {
    w: 123,  // changing copy.w didn't affect obj.w
    x: {
        y: 654,  // changing copy.x.y also changed obj.x.y
        z: 789
    }
};

Vous pouvez éviter cela avec une copie profonde. La copie profonde récure dans chaque objet et tableau imbriqué (et Date dans le code d'Armand) pour faire des copies de ces objets de la même manière qu'elle a fait une copie de l'objet de niveau supérieur. Ainsi, en changeant copy.x.y n'affecterait pas obj.x.y .

Réponse courte : En cas de doute, il est préférable d'avoir une copie profonde.

67voto

kernowcode Points 51

J'ai trouvé que l'utilisation de JSON fonctionne mais attention aux références circulaires.

var newInstance = JSON.parse(JSON.stringify(firstInstance));

63voto

Kishor Patil Points 211

newVariable = originalVariable.valueOf();

pour les objets que vous pouvez utiliser, b = Object.assign({},a);

36voto

marcosh Points 4757

La question est déjà résolue depuis longtemps, mais pour référence future une solution possible est

b = a.slice(0);

Attention, cela ne fonctionne correctement que si a est un tableau non imbriqué de nombres et de chaînes de caractères.

18voto

Armand Points 1804

La raison en est simple. JavaScript utilise des références, donc lorsque vous assignez b = a vous attribuez une référence à b Ainsi, lors de la mise à jour a vous mettez également à jour b

J'ai trouvé ce sur stackoverflow et permettra d'éviter ce genre de choses à l'avenir en appelant simplement cette méthode si vous voulez faire une copie profonde d'un objet.

function clone(obj) {
    // Handle the 3 simple types, and null or undefined
    if (null == obj || "object" != typeof obj) return obj;

    // Handle Date
    if (obj instanceof Date) {
        var copy = new Date();
        copy.setTime(obj.getTime());
        return copy;
    }

    // Handle Array
    if (obj instanceof Array) {
        var copy = [];
        for (var i = 0, len = obj.length; i < len; i++) {
            copy[i] = clone(obj[i]);
        }
        return copy;
    }

    // Handle Object
    if (obj instanceof Object) {
        var copy = {};
        for (var attr in obj) {
            if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
        }
        return copy;
    }

    throw new Error("Unable to copy obj! Its type isn't supported.");
}

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