Copia il valore di una variabile in un'altra


100

Ho una variabile che ha un oggetto JSON come valore. Assegno direttamente questa variabile a un'altra variabile in modo che condividano lo stesso valore. È così che funziona:

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

Funziona ed entrambi hanno lo stesso valore. Uso un mousemovegestore di eventi per aggiornare btramite la mia app. Facendo clic su un pulsante, voglio ripristinare bil valore originale, ovvero il valore memorizzato in a.

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

Dopo questo, se utilizzo lo stesso mousemovegestore di eventi, aggiorna entrambi ae b, quando in precedenza, si aggiornava solo bcome previsto.

Sono perplesso su questo problema! Cosa c'è che non va qui?


Per favore mostra il tuo gestore di rimozione del mouse. Inoltre, dato che è astato impostato da .val()presumo che sia JSON (una stringa), non un oggetto, è vero? Usi JSON.parse(a)ad un certo punto per ottenere un oggetto reale?
nnnnnn

Sì, è una stringa ma la uso $.parseJSONper convertirla in un oggetto. STRUTTURA: { 'key': {...}, 'key': {...}, ...}. Spiacente, non posso inserire alcun codice qui, non consentito nel mio posto di lavoro!
Rutwick Gangurde

1
quindi è aun oggetto?
Armand

1
Sembra un problema di scoping .. è auna variabile globale?
msturdy

2
Il codice che hai mostrato ha solo stringhe. Se stai parlando di oggetti, più variabili potrebbero fare riferimento allo stesso oggetto e quindi quell'oggetto potrebbe essere mutato tramite una qualsiasi delle variabili. Pertanto, senza vedere altro del codice che manipola le variabili (dove $.parseJSON()entra in gioco?), È difficile dire quale sia il problema. Per quanto riguarda le regole del tuo posto di lavoro, non devi pubblicare il tuo codice reale per intero, basta inventare un esempio più breve e generico che dimostri il problema (e, idealmente, includere un collegamento a una demo dal vivo su jsfiddle.net ) .
nnnnnn

Risposte:


198

È importante capire cos'è il file = fa e cosa non fa operatore in JavaScript.

L' =operatore non fa una copia dei dati.

L' =operatore crea un nuovo riferimento allo stesso dati.

Dopo aver eseguito il codice originale:

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

ae bora sono due nomi diversi per lo stesso oggetto .

Qualsiasi modifica apportata al contenuto di questo oggetto verrà visualizzata in modo identico indipendentemente dal fatto che lo si faccia riferimento tramite la avariabile o ilb variabile. Sono lo stesso oggetto.

Quindi, quando in seguito proverai a "ripristinare" bl' aoggetto originale con questo codice:

b = a;

Il codice in realtà non fa nulla , perché ae bsono la stessa identica cosa. Il codice è lo stesso come se avessi scritto:

b = b;

che ovviamente non farà nulla.

Perché il tuo nuovo codice funziona?

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

Qui stai creando un oggetto nuovo di zecca con l' {...}oggetto letterale. Questo nuovo oggetto non è lo stesso del tuo vecchio oggetto. Quindi ora stai impostandob come riferimento a questo nuovo oggetto, che fa quello che vuoi.

Per gestire qualsiasi oggetto arbitrario, puoi utilizzare una funzione di clonazione di oggetti come quella elencata nella risposta di Armand, o poiché stai usando jQuery usa semplicemente la $.extend()funzione . Questa funzione creerà una copia superficiale o una copia completa di un oggetto. (Non confonderlo con il $().clone()metodo che serve per copiare elementi DOM, non oggetti.)

Per una copia superficiale:

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

O una copia completa:

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

Qual è la differenza tra una copia superficiale e una copia profonda? Una copia superficiale è simile al codice che crea un nuovo oggetto con un oggetto letterale. Crea un nuovo oggetto di primo livello contenente riferimenti alle stesse proprietà dell'oggetto originale.

Se il tuo oggetto contiene solo tipi primitivi come numeri e stringhe, una copia completa e una copia superficiale faranno esattamente la stessa cosa. Ma se il tuo oggetto contiene altri oggetti o array annidati al suo interno, una copia superficiale non copia quegli oggetti annidati, ma semplicemente crea riferimenti ad essi. Quindi potresti avere lo stesso problema con gli oggetti nidificati che avevi con il tuo oggetto di primo livello. Ad esempio, dato questo oggetto:

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

Se esegui una copia superficiale di quell'oggetto, la xproprietà del tuo nuovo oggetto è lo stesso xoggetto dell'originale:

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

Ora i tuoi oggetti avranno questo aspetto:

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

Puoi evitarlo con una copia completa. La copia profonda ricorre in ogni oggetto e array annidato (e Data nel codice di Armand) per fare copie di quegli oggetti nello stesso modo in cui ha fatto una copia dell'oggetto di primo livello. Quindi il cambiamento copy.x.ynon avrebbe effettoobj.x.y .

Risposta breve: in caso di dubbio, probabilmente vuoi una copia completa.


Grazie, questo è quello che stavo cercando. Qualche modo per aggirarlo? Nella mia situazione attuale, è stato facile risolverlo poiché avevo solo 2 proprietà. Ma potrebbero esserci oggetti più grandi.
Rutwick Gangurde

1
La risposta di Armand ha una funzione di clonazione di oggetti che farà il trucco. O poiché stai usando jQuery, puoi usare la $.extend()funzione integrata. Dettagli sopra. :-)
Michael Geary

Ottima spiegazione Michael!
Rutwick Gangurde

7
@MichaelGeary Potrebbe essere pignolo ma la regola non si applica solo agli oggetti e non alle variabili primitive? potrebbe valere la pena notare nella risposta
Jonathan dos Santos

La soluzione JS a questo è nella risposta di Armands come ha detto Michael Geary. =
AlexanderGriffin

57

Ho scoperto che l'uso di JSON funziona ma guarda il nostro per i riferimenti circolari

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

2
Mi rendo conto che è un po 'tardi, ma c'è un problema con questo metodo? Sembra funzionare sorprendentemente al primo tentativo.
Mankind1023

2
Questo è ottimo in quasi tutte le situazioni, tuttavia se lavori con dati che hanno il potenziale per essere ciculari, il tuo programma si bloccherà. un po 'di codice per illustrare: let a = {}; let b = {a:a}; a.b = b; JSON.stringify(a)darà un TypeError
schu34

Questa soluzione funziona alla grande. Tuttavia, quanto è costoso durante l'elaborazione? Sembra che funzioni per doppia negazione.
Abel Callejo,

29

la questione è già risolta da molto tempo, ma per riferimento futuro una possibile soluzione lo è

b = a.slice(0);

Attenzione, funziona correttamente solo se a è una matrice non annidata di numeri e stringhe


23

newVariable = originalVariable.valueOf();

per gli oggetti che puoi usare, b = Object.assign({},a);


3
per gli oggetti che puoi usare, b = Object.assign ({}, a);
Kishor Patil

15

Il motivo è semplice. JavaScript utilizza i riferimenti, quindi quando si assegna b = asi assegna un riferimento a bcosì durante l'aggiornamento asi aggiorna ancheb

Ho trovato questo su StackOverflow e aiuterà a prevenire le cose come questo in futuro, semplicemente chiamando questo metodo se si vuole fare una copia completa di un oggetto.

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

Grazie! Ho visto quel post prima di pubblicare una nuova domanda. Ma non ero sicuro che ciò avrebbe aiutato nel mio scenario. Si scopre che è quello che mi serve.
Rutwick Gangurde

Non dovrebbe essere tutto tranne il primo if (obj instanceof x) { ... }se?
Zac

@Zac Non in questo caso. Ogni if-body (di interesse) restituisce un valore. (esce dalla funzione prima del prossimo if)
Bitterblue

8

Non capisco perché le risposte siano così complesse. In Javascript, le primitive (stringhe, numeri, ecc.) Vengono passate per valore e copiate. Gli oggetti, inclusi gli array, vengono passati per riferimento. In ogni caso, l'assegnazione di un nuovo valore o riferimento a un oggetto "a" non cambierà "b". Ma la modifica del contenuto di "a" cambierà il contenuto di "b".

var a = 'a'; var b = a; a = 'c'; // b === 'a'

var a = {a:'a'}; var b = a; a = {c:'c'}; // b === {a:'a'} and a = {c:'c'}

var a = {a:'a'}; var b = a; a.a = 'c'; // b.a === 'c' and a.a === 'c'

Incolla una qualsiasi delle righe precedenti (una alla volta) nel nodo o in qualsiasi console javascript del browser. Quindi digita una variabile e la console mostrerà il suo valore.


4

Per stringhe o valori di input puoi semplicemente usare questo:

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

Perché sottostringere una primitiva? Basta assegnarlo, copia la stringa. Solo gli oggetti e gli array (che sono oggetti) vengono passati per riferimento.
Trenton

2

La maggior parte delle risposte qui stanno usando metodi incorporati o usando librerie / framework. Questo semplice metodo dovrebbe funzionare bene:

function copy(x) {
    return JSON.parse( JSON.stringify(x) );
}

// Usage
var a = 'some';
var b = copy(a);
a += 'thing';

console.log(b); // "some"

var c = { x: 1 };
var d = copy(c);
c.x = 2;

console.log(d); // { x: 1 }

Genio! Altri metodi trasformano tutto in dizionario, inclusi gli array al loro interno. Con questo clono l'oggetto e il contenuto rimane intatto, a differenza di questo:Object.assign({},a)
Tiki

0

L'ho risolto da solo per il momento. Il valore originale ha solo 2 proprietà secondarie. Ho riformato un nuovo oggetto con le proprietà da ae poi l'ho assegnato a b. Ora il mio gestore eventi si aggiorna solo be il mio originale arimane così com'è.

var a = { key1: 'value1', key2: 'value2' },
    b = a;

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

    //WIN
    b = { key1: a.key1, key2: a.key2 };
});

Funziona bene. Non ho cambiato una sola riga in nessuna parte del mio codice tranne quanto sopra, e funziona proprio come volevo. Quindi, credimi, nient'altro si stava aggiornando a.


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.