Skopiuj wartość zmiennej do innej


100

Mam zmienną, której wartością jest obiekt JSON. Bezpośrednio przypisuję tę zmienną do innej zmiennej, aby miały tę samą wartość. Tak to działa:

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

To działa i oba mają tę samą wartość. Używam programu mousemoveobsługi zdarzeń do aktualizacji bza pośrednictwem mojej aplikacji. Po kliknięciu przycisku chcę przywrócić bpierwotną wartość, czyli wartość przechowywaną w a.

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

Następnie, jeśli używam tego samego programu mousemoveobsługi zdarzeń, aktualizuje on oba, aa bwcześniej aktualizował tylko bzgodnie z oczekiwaniami.

Jestem zaskoczony tym problemem! Co tu jest nie tak?


Proszę pokazać swój moduł obsługi myszy. Ponadto, biorąc pod uwagę, że azostał ustawiony z .val(), zakładam, że jest to JSON (ciąg znaków), a nie obiekt - czy to prawda? Czy używasz JSON.parse(a)kiedyś, aby uzyskać rzeczywisty obiekt?
nnnnnn

Tak, jest to ciąg znaków, ale używam go $.parseJSONdo konwersji na obiekt. Struktura: { 'key': {...}, 'key': {...}, ...}. Przepraszamy, nie mogę tutaj umieścić żadnego kodu, niedozwolone w moim miejscu pracy!
Rutwick Gangurde

1
więc to ajest obiekt?
Armand

1
Wygląda na to, że problem dotyczy zakresu… czy jest azmienną globalną?
msturdy,

2
Kod, który pokazałeś, zawiera tylko ciągi. Jeśli mówisz o obiektach, to wiele zmiennych może odnosić się do tego samego obiektu, a więc obiekt może zostać zmutowany przez dowolną ze zmiennych. Dlatego nie widząc więcej kodu, który manipuluje zmiennymi (skąd się to $.parseJSON()bierze?), Trudno powiedzieć, na czym polega problem. Jeśli chodzi o zasady dotyczące miejsca pracy, nie musisz publikować całego rzeczywistego kodu, po prostu wymyśl krótszy i bardziej ogólny przykład, który demonstruje problem (i najlepiej, aby zawierał link do demonstracji na żywo na jsfiddle.net ) .
nnnnnn

Odpowiedzi:


201

Ważne jest, aby zrozumieć, co robi =operator w JavaScript, a czego nie.

=Operator nie robi kopii danych.

=Operator tworzy nowe odniesienie do samego danych.

Po uruchomieniu oryginalnego kodu:

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

ai bsą teraz dwiema różnymi nazwami dla tego samego obiektu .

Wszelkie zmiany wprowadzone w zawartości tego obiektu będą widoczne identycznie, niezależnie od tego, czy odwołujesz się do niego za pośrednictwem azmiennej, czy bzmiennej. To ten sam obiekt.

Tak więc, gdy później spróbujesz „przywrócić” boryginalny aobiekt za pomocą tego kodu:

b = a;

Kod właściwie nic nie robi , ponieważ ai bsą dokładnie tym samym. Kod jest taki sam, jak gdybyś napisał:

b = b;

co oczywiście nic nie da.

Dlaczego twój nowy kod działa?

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

Tutaj tworzysz zupełnie nowy obiekt z {...}literałem obiektu. Ten nowy obiekt nie jest tym samym, co stary obiekt. Więc teraz ustawiasz bjako odniesienie do tego nowego obiektu, który robi to, co chcesz.

Aby obsłużyć dowolny obiekt, możesz użyć funkcji klonowania obiektu, takiej jak ta wymieniona w odpowiedzi Armanda, lub ponieważ używasz jQuery, po prostu użyj tej $.extend()funkcji . Ta funkcja utworzy płytką kopię lub głęboką kopię obiektu. (Nie myl tego z $().clone()metodą kopiowania elementów DOM, a nie obiektów).

W przypadku płytkiej kopii:

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

Lub głęboka kopia:

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

Jaka jest różnica między płytką kopią a głęboką kopią? Płytka kopia jest podobna do kodu, który tworzy nowy obiekt z literałem obiektu. Tworzy nowy obiekt najwyższego poziomu zawierający odniesienia do tych samych właściwości, co oryginalny obiekt.

Jeśli twój obiekt zawiera tylko prymitywne typy, takie jak liczby i łańcuchy, głęboka kopia i płytka kopia zrobią dokładnie to samo. Ale jeśli twój obiekt zawiera inne obiekty lub tablice zagnieżdżone w nim, to płytka kopia nie kopiuje tych zagnieżdżonych obiektów, a jedynie tworzy do nich odniesienia. Więc możesz mieć ten sam problem z zagnieżdżonymi obiektami, który miałeś z obiektem najwyższego poziomu. Na przykład biorąc pod uwagę ten obiekt:

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

Jeśli zrobisz płytką kopię tego obiektu, wówczas xwłaściwość nowego obiektu będzie taka sama xjak oryginalnego:

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

Teraz twoje obiekty będą wyglądać tak:

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

Możesz tego uniknąć dzięki głębokiej kopii. Głęboka kopia powtarza się do każdego zagnieżdżonego obiektu i tablicy (oraz daty w kodzie Armanda), aby utworzyć kopie tych obiektów w ten sam sposób, w jaki utworzyła kopię obiektu najwyższego poziomu. Więc zmiana copy.x.ynie wpłynie obj.x.y.

Krótka odpowiedź: w razie wątpliwości prawdopodobnie potrzebujesz głębokiej kopii.


Dzięki, właśnie tego szukałem. Jakoś to obejść? W mojej obecnej sytuacji było to łatwe do naprawienia, ponieważ miałem tylko 2 właściwości. Ale mogą istnieć większe obiekty.
Rutwick Gangurde

1
Odpowiedź Armanda zawiera funkcję klonowania obiektów, która załatwi sprawę. A ponieważ używasz jQuery, możesz użyć wbudowanej $.extend()funkcji. Szczegóły powyżej. :-)
Michael Geary

Świetne wyjaśnienie Michael!
Rutwick Gangurde

7
@MichaelGeary To może być chwytanie dziur w dziury, ale czy reguła nie dotyczy tylko obiektów, a nie zmiennych pierwotnych? warto zauważyć w odpowiedzi
Jonathan dos Santos

Rozwiązanie JS jest w odpowiedzi Armands, jak powiedział Michael Geary. =
AlexanderGriffin

57

Odkryłem, że używanie JSON działa, ale uważaj na nasze odwołania cykliczne

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

2
Zdaję sobie sprawę, że to trochę za późno, ale czy jest jakiś problem z tą metodą? Wydaje się, że działa niesamowicie za pierwszym razem.
Mankind1023

2
Jest to świetne w prawie każdej sytuacji, jednak jeśli pracujesz z danymi, które mogą być cykliczne, spowoduje to zawieszenie programu. trochę kodu do zilustrowania: let a = {}; let b = {a:a}; a.b = b; JSON.stringify(a)da TypeError
schu34

To rozwiązanie działa świetnie. Jednak jak drogie jest to podczas przetwarzania? Wygląda na to, że działa to na zasadzie podwójnej negacji.
Abel Callejo

30

pytanie zostało już rozwiązane od dłuższego czasu, ale w przyszłości możliwe jest rozwiązanie

b = a.slice(0);

Uważaj, to działa poprawnie tylko wtedy, gdy a jest niezagnieżdżoną tablicą liczb i ciągów


24

newVariable = originalVariable.valueOf();

dla przedmiotów, których możesz użyć, b = Object.assign({},a);


3
dla obiektów, których możesz użyć, b = Object. assign ({}, a);
Kishor Patil

15

Powód jest prosty. JavaScript używa referencji, więc kiedy przypisujesz b = a, przypisujesz odwołanie, bwięc podczas aktualizacji arównież aktualizujeszb

Znalazłem to na stackoverflow i pomogę zapobiec takim sytuacjom w przyszłości, po prostu wywołując tę ​​metodę, jeśli chcesz wykonać głęboką kopię obiektu.

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

Dzięki! Widziałem ten post, zanim opublikowałem nowe pytanie. Ale nie byłem pewien, czy to pomoże w moim scenariuszu. Okazuje się, że tego właśnie potrzebuję.
Rutwick Gangurde

Czy nie powinno if (obj instanceof x) { ... }być inaczej, jeśli tylko pierwszy ?
Zac

@Zac Nie w tym przypadku. Każdy element if-body (będący przedmiotem zainteresowania) zwraca wartość. (wychodzi z funkcji przed następnym if)
Bitterblue

8

Nie rozumiem, dlaczego odpowiedzi są tak złożone. W Javascript prymitywy (łańcuchy, liczby itp.) Są przekazywane przez wartość i kopiowane. Obiekty, w tym tablice, są przekazywane przez odwołanie. W każdym razie przypisanie nowej wartości lub odniesienia do obiektu do „a” nie zmieni „b”. Ale zmiana zawartości „a” zmieni zawartość „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'

Wklej dowolną z powyższych linii (pojedynczo) do węzła lub dowolnej konsoli javascript przeglądarki. Następnie wpisz dowolną zmienną, a konsola pokaże jej wartość.


4

W przypadku ciągów lub wartości wejściowych możesz po prostu użyć tego:

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

Dlaczego miałbyś podciągać prymityw? Po prostu przypisz, kopiuje ciąg. Tylko obiekty i tablice (które są obiektami) są przekazywane przez odwołanie.
Trenton D. Adams,

2

Większość odpowiedzi tutaj wykorzystuje wbudowane metody lub biblioteki / frameworki. Ta prosta metoda powinna działać dobrze:

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 }

Geniusz! Inne metody zamieniają wszystko w słownik, w tym tablice w nich. Dzięki temu klonuję obiekt, a zawartość pozostaje nienaruszona, w przeciwieństwie do tego:Object.assign({},a)
Tiki

0

Na razie sam to rozwiązałem. Oryginalna wartość ma tylko 2 właściwości podrzędne. Zreformowałem nowy obiekt z właściwościami od, aa następnie przypisałem go do b. Teraz mój program obsługi zdarzeń aktualizuje się tylko b, a mój oryginał apozostaje taki, jaki jest.

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

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

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

To działa dobrze. Nie zmieniłem ani jednej linii w moim kodzie poza powyższym i działa tak, jak chciałem. Więc zaufaj mi, nic innego się nie aktualizowało a.


Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.