Salin nilai variabel ke yang lain

100

Saya memiliki variabel yang memiliki objek JSON sebagai nilainya. Saya langsung menetapkan variabel ini ke beberapa variabel lain sehingga mereka memiliki nilai yang sama. Begini Cara kerjanya:

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

Ini berfungsi dan keduanya memiliki nilai yang sama. Saya menggunakan mousemoveevent handler untuk mengupdate bseluruh aplikasi saya. Pada satu klik tombol, saya ingin kembali bke nilai asli, artinya nilai yang disimpan a.

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

Setelah ini, jika saya menggunakan mousemoveevent handler yang sama , ia memperbarui keduanya adan b, ketika sebelumnya hanya memperbarui bseperti yang diharapkan.

Saya bingung tentang masalah ini! Ada apa disini?

Rutwick Gangurde
sumber
Tolong tunjukkan penangan mousemove Anda. Juga, mengingat yang aditetapkan dari .val()saya menganggap itu adalah JSON (string), bukan objek - apakah itu benar? Apakah Anda menggunakan JSON.parse(a)suatu saat untuk mendapatkan objek yang sebenarnya?
nnnnnn
Ya itu adalah string tapi saya gunakan $.parseJSONuntuk mengubahnya menjadi objek. Struktur: { 'key': {...}, 'key': {...}, ...}. Maaf tidak dapat memposting kode apa pun di sini, tidak diizinkan di tempat kerja saya!
Rutwick Gangurde
1
jadi apakah asebuah objek?
Armand
1
Sepertinya masalah pelingkupan .. apakah avariabel global?
msturdy
2
Kode yang Anda tunjukkan hanya memiliki string. Jika Anda membicarakan objek, maka beberapa variabel dapat merujuk ke objek yang sama dan objek tersebut dapat dimutasi melalui variabel mana pun. Oleh karena itu tanpa melihat lebih banyak kode yang memanipulasi variabel (dari mana datangnya $.parseJSON()?) Sulit untuk mengatakan apa masalahnya. Mengenai aturan tempat kerja Anda, Anda tidak perlu memposting kode asli Anda secara lengkap, cukup buat contoh yang lebih pendek dan lebih umum yang menunjukkan masalah tersebut (dan, idealnya, sertakan tautan ke demo langsung di jsfiddle.net ) .
nnnnnn

Jawaban:

201

Penting untuk memahami apa yang =dilakukan dan tidak dilakukan oleh operator di JavaScript.

The =operator tidak membuat salinan data.

The =Operator menciptakan baru referensi ke yang sama data.

Setelah Anda menjalankan kode asli Anda:

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

adan bsekarang ada dua nama berbeda untuk objek yang sama .

Setiap perubahan yang Anda lakukan pada konten objek ini akan terlihat sama, baik Anda mereferensikannya melalui avariabel atau bvariabel. Mereka adalah objek yang sama.

Jadi, ketika Anda nanti mencoba untuk "kembali" bke aobjek asli dengan kode ini:

b = a;

Kode sebenarnya tidak melakukan apa - apa , karena adan bmerupakan hal yang persis sama. Kode tersebut sama seperti jika Anda menulis:

b = b;

yang jelas tidak akan melakukan apapun.

Mengapa kode baru Anda berfungsi?

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

Di sini Anda membuat objek baru dengan {...}literal objek. Objek baru ini tidak sama dengan objek lama Anda. Jadi Anda sekarang menyetel bsebagai referensi ke objek baru ini, yang melakukan apa yang Anda inginkan.

Untuk menangani objek arbitrer, Anda dapat menggunakan fungsi kloning objek seperti yang tercantum dalam jawaban Armand, atau karena Anda menggunakan jQuery, cukup gunakan $.extend()fungsi tersebut . Fungsi ini akan membuat salinan dangkal atau salinan dalam dari suatu objek. (Jangan bingung ini dengan $().clone()metode untuk menyalin elemen DOM, bukan objek.)

Untuk salinan dangkal:

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

Atau salinan dalam:

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

Apa perbedaan antara salinan dangkal dan salinan dalam? Salinan dangkal mirip dengan kode Anda yang membuat objek baru dengan literal objek. Ini membuat objek tingkat atas baru yang berisi referensi ke properti yang sama seperti objek aslinya.

Jika objek Anda hanya berisi tipe primitif seperti angka dan string, salinan dalam dan salinan dangkal akan melakukan hal yang persis sama. Tetapi jika objek Anda berisi objek atau larik lain yang bersarang di dalamnya, salinan dangkal tidak akan menyalin objek bertingkat itu, itu hanya membuat referensi ke sana. Jadi, Anda bisa memiliki masalah yang sama dengan objek bersarang yang Anda miliki dengan objek level teratas. Misalnya, diberikan objek ini:

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

Jika Anda melakukan penyalinan dangkal dari objek tersebut, maka xproperti objek baru Anda adalah objek yang sama xdari aslinya:

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

Sekarang objek Anda akan terlihat seperti ini:

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

Anda dapat menghindari ini dengan salinan dalam. Salinan dalam berulang ke dalam setiap objek dan larik bersarang (dan Tanggal dalam kode Armand) untuk membuat salinan objek tersebut dengan cara yang sama seperti membuat salinan objek tingkat atas. Jadi, perubahan copy.x.ytidak akan berpengaruh obj.x.y.

Jawaban singkat: Jika ragu, Anda mungkin menginginkan salinan yang mendalam.

Michael Geary
sumber
Terima kasih, inilah yang saya cari. Adakah cara untuk mengatasinya? Dalam situasi saya saat ini, mudah untuk diperbaiki karena saya hanya memiliki 2 properti. Tapi mungkin ada benda yang lebih besar.
Rutwick Gangurde
1
Jawaban Armand memiliki fungsi klon objek yang akan melakukan triknya. Atau karena Anda menggunakan jQuery, Anda dapat menggunakan $.extend()fungsi bawaan. Detail di atas. :-)
Michael Geary
Penjelasan bagus Michael!
Rutwick Gangurde
7
@MichaelGeary Mungkin nitpicking tapi bukankah aturan hanya berlaku untuk objek dan bukan variabel primitif? mungkin perlu diperhatikan dalam jawabannya
Jonathan dos Santos
Solusi JS untuk ini ada dalam jawaban Armands seperti yang dikatakan Michael Geary. =
AlexanderGriffin
57

Saya menemukan menggunakan JSON berfungsi tetapi perhatikan referensi melingkar kami

var newInstance = JSON.parse(JSON.stringify(firstInstance));
kernowcode.dll
sumber
2
Saya menyadari ini agak terlambat, tetapi apakah ada masalah dengan metode ini? Tampaknya bekerja dengan luar biasa pada percobaan pertama.
Mankind1023
2
Ini bagus di hampir setiap situasi, namun jika Anda bekerja dengan data yang berpotensi terkikis, program Anda akan mogok. beberapa kode untuk diilustrasikan: let a = {}; let b = {a:a}; a.b = b; JSON.stringify(a)akan memberikan TypeError
schu34
Solusi ini bekerja dengan baik. Namun, seberapa mahal biaya ini selama pemrosesan? Tampaknya ini bekerja dengan negasi ganda.
Abel Callejo
30

pertanyaannya sudah terpecahkan sejak waktu yang cukup lama, tetapi untuk referensi di masa mendatang solusi yang mungkin adalah

b = a.slice(0);

Hati-hati, ini berfungsi dengan benar hanya jika a adalah larik angka dan string yang tidak bersarang

marcosh
sumber
24

newVariable = originalVariable.valueOf();

untuk objek yang dapat Anda gunakan, b = Object.assign({},a);

Kishor Patil
sumber
3
untuk objek yang bisa Anda gunakan, b = Object.assign ({}, a);
Kishor Patil
15

Alasannya sederhana. JavaScript menggunakan referensi, jadi ketika Anda menetapkan b = aAnda menetapkan referensi bsehingga saat memperbarui aAnda juga memperbaruib

Saya menemukan ini di stackoverflow dan akan membantu mencegah hal-hal seperti ini di masa mendatang hanya dengan memanggil metode ini jika Anda ingin melakukan salinan mendalam dari suatu objek.

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.");
}
Armand
sumber
Terima kasih! Saya melihat postingan itu sebelum saya memposting pertanyaan baru. Tapi saya tidak yakin apakah itu akan membantu skenario saya. Ternyata itulah yang saya butuhkan.
Rutwick Gangurde
Bukankah semua kecuali yang pertama if (obj instanceof x) { ... }harus menjadi yang lain?
Zac
@Zac Tidak dalam kasus ini. Setiap if-body (of interest) mengembalikan sebuah nilai. (keluar dari fungsi sebelum berikutnya jika)
Bitterblue
8

Saya tidak mengerti mengapa jawabannya begitu rumit. Dalam Javascript, primitif (string, angka, dll) diteruskan oleh nilai, dan disalin. Objek, termasuk array, diteruskan dengan referensi. Bagaimanapun, penugasan nilai baru atau referensi objek ke 'a' tidak akan mengubah 'b'. Tetapi mengubah konten 'a' akan mengubah konten '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'

Tempel salah satu baris di atas (satu per satu) ke dalam node atau konsol javascript browser apa pun. Kemudian ketik variabel apa saja dan konsol akan menunjukkan nilainya.

Trenton D. Adams
sumber
4

Untuk string atau nilai input Anda cukup menggunakan ini:

var a = $('#some_hidden_var').val(),
b = a.substr(0);
Tim
sumber
Mengapa Anda substring primitif? Tetapkan saja, itu menyalin string. Hanya objek, dan array (yang merupakan objek) yang diteruskan oleh referensi.
Trenton D. Adams
2

Sebagian besar jawaban di sini menggunakan metode bawaan atau menggunakan perpustakaan / kerangka kerja. Metode sederhana ini seharusnya berfungsi dengan baik:

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 }
Lasse Brustad
sumber
Jenius! Metode lain mengubah segalanya menjadi kamus, termasuk array di dalamnya. Dengan ini saya mengkloning objek dan isinya tetap utuh, tidak seperti ini:Object.assign({},a)
Tiki
0

Saya menyelesaikannya sendiri untuk saat ini. Nilai asli hanya memiliki 2 sub-properti. Saya mereformasi objek baru dengan properti dari adan kemudian menugaskannya b. Sekarang event handler saya hanya memperbarui b, dan yang asli atetap seperti itu.

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

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

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

Ini bekerja dengan baik. Saya belum mengubah satu baris pun di kode saya kecuali yang di atas, dan berfungsi seperti yang saya inginkan. Jadi, percayalah, tidak ada lagi yang memperbarui a.

Rutwick Gangurde
sumber
0

Solusi untuk AngularJS :

$scope.targetObject = angular.copy($scope.sourceObject)
Tony Sepia
sumber