Bagaimana cara kerja tugas variabel di JavaScript?

97

Jadi saya bermain-main beberapa hari yang lalu hanya untuk melihat dengan tepat bagaimana tugas massal bekerja di JavaScript.

Pertama saya mencoba contoh ini di konsol:

a = b = {};
a.foo = 'bar';
console.log(b.foo);

Hasilnya adalah "bar" yang ditampilkan dalam sebuah peringatan. Itu cukup adil, adan bsebenarnya hanyalah alias untuk objek yang sama. Lalu saya berpikir, bagaimana saya bisa membuat contoh ini lebih sederhana.

a = b = 'foo';
a = 'bar';
console.log(b);

Itu kurang lebih sama, bukan? Nah kali ini, hasilnya footidak barseperti yang saya harapkan dari perilaku contoh pertama.

Mengapa ini terjadi?

NB Contoh ini bisa lebih disederhanakan lagi dengan kode berikut:

a = {};
b = a;
a.foo = 'bar';
console.log(b.foo);

a = 'foo';
b = a;
a = 'bar';
console.log(b);

(Saya menduga bahwa JavaScript memperlakukan primitif seperti string dan integer secara berbeda dengan hash. Hash mengembalikan penunjuk sementara primitif "inti" mengembalikan salinan dirinya sendiri)

Chris Lloyd
sumber
Menjelaskan tugas di sini: syntaxsuccess.com/viewarticle/…
TGH

Jawaban:

114

Dalam contoh pertama, Anda menyetel properti dari objek yang sudah ada. Dalam contoh kedua, Anda menetapkan objek baru.

a = b = {};

adan bsekarang penunjuk ke objek yang sama. Jadi, saat Anda melakukannya:

a.foo = 'bar';

Ini set b.foojuga sejak adan bmenunjuk ke objek yang sama.

Namun!

Jika Anda melakukan ini sebagai gantinya:

a = 'bar';

Anda mengatakan itu amenunjuk ke objek yang berbeda sekarang. Ini tidak berpengaruh pada apa yang adisebutkan sebelumnya.

Dalam JavaScript, menetapkan variabel dan menetapkan properti adalah 2 operasi berbeda. Yang terbaik adalah memikirkan variabel sebagai penunjuk ke objek, dan ketika Anda menetapkan langsung ke variabel, Anda tidak memodifikasi objek apa pun, hanya menunjuk variabel Anda ke objek yang berbeda.

Tapi menugaskan properti, seperti a.foo, akan mengubah objek yang adituju. Ini, tentu saja, juga mengubah semua referensi lain yang mengarah ke objek ini hanya karena semuanya menunjuk ke objek yang sama.

Alex Wayne
sumber
3
"Anda mengatakan bahwa sekarang menunjuk ke objek yang berbeda." Tidak, jangan gunakan kata objek. String bukanlah objek dalam JavaScript.
Nosredna
9
Mungkin string secara teknis bukan tipe javascript "Object", tapi bisa dianggap sebagai objek dalam pengertian OO.
Alex Wayne
2
@Squeegy: string adalah primitif, bukan objek: Anda tidak dapat menetapkan properti sewenang-wenang ke string! Mereka hanya berperilaku seperti objek karena apa yang disebut autoboxing di Java
Christoph
11
Tetapi string memiliki metode dan properti, dan prototipe String pasti dapat dimodifikasi. Mereka benar-benar bertindak seperti benda.
Cameron Martin
9
@ddlshack: Seperti yang dijelaskan Christoph, ini karena autoboxing. Jika Anda mendefinisikan string melalui var foo = new String('foo');, maka itu akan menjadi objek string (dan typeofakan mengkonfirmasi ini). Tetapi jika Anda mendeklarasikannya melalui string literal, maka mereka adalah string primitif. Lihat: developer.mozilla.org/en/JavaScript/Reference/Global_Objects/…
Lèse majesté
26

Pertanyaan Anda telah dijawab dengan memuaskan oleh Squeegy - ini tidak ada hubungannya dengan objek vs. primitif, tetapi dengan penugasan ulang variabel vs. properti pengaturan dalam objek referensi yang sama.

Sepertinya ada banyak kebingungan tentang tipe JavaScript dalam jawaban dan komentar, jadi berikut adalah pengantar kecil untuk sistem tipe JavaScript:

Dalam JavaScript, ada dua jenis nilai yang berbeda secara fundamental: primitif dan objek (dan tidak ada yang namanya 'hash').

String, angka, dan boolean serta nulldan undefinedbersifat primitif, objek adalah segala sesuatu yang dapat memiliki properti. Bahkan array dan fungsi adalah objek biasa dan oleh karena itu dapat menampung properti arbitrer. Mereka hanya berbeda dalam properti [[Class]] internal (fungsi tambahan memiliki properti yang disebut [[Call]] dan [[Construct]], tapi hei, itu detailnya).

Alasan nilai primitif dapat berperilaku seperti objek adalah karena autoboxing, tetapi primitif itu sendiri tidak dapat menyimpan properti apa pun.

Berikut ini contohnya:

var a = 'quux';
a.foo = 'bar';
document.writeln(a.foo);

Ini akan menampilkan undefined: amemegang nilai primitif, yang dipromosikan menjadi objek saat menetapkan properti foo. Tetapi objek baru ini segera dibuang, sehingga nilainya foohilang.

Pikirkan seperti ini:

var a = 'quux';
new String(a).foo = 'bar'; // we never save this new object anywhere!
document.writeln(new String(a).foo); // a completly new object gets created
Christoph
sumber
Halaman Mozilla Foundation 'Pengenalan ulang ke JavaScript (tutorial JS)' menjelaskan objek JavaScript "sebagai kumpulan sederhana dari pasangan nama-nilai. Dengan demikian, mereka mirip dengan ...", lalu diikuti dengan daftar kamus, hash , tabel hash, dan peta hash dari berbagai bahasa pemrograman. Halaman yang sama mendeskripsikan referensi properti objek sebagai pencarian tabel hash. Jadi objek adalah segalanya seperti tabel 'hash'. Ini tidak membatalkan informasi berguna lainnya, tetapi karakterisasi asli Chris Lloyd tidaklah tidak akurat.
C Perkins
2

Anda kurang lebih benar kecuali bahwa yang Anda sebut sebagai "hash" sebenarnya hanyalah sintaks singkat untuk sebuah Objek.

Dalam contoh pertama, a dan b merujuk ke objek yang sama. Dalam contoh kedua, Anda mengubah a untuk merujuk ke sesuatu yang lain.

Kevin
sumber
Jadi mengapa standar ganda untuk Object?
Chris Lloyd
Ini bukan standar ganda. Dalam contoh pertama, a dan b masih merujuk ke objek yang sama, Anda hanya memodifikasi properti dari objek itu. Dalam contoh kedua, Anda menunjuk ke objek yang berbeda.
Kevin
1
Tidak, perbedaannya adalah dalam kasus kedua, Anda berurusan dengan string, bukan objek.
Nosredna
1
Untuk memperjelas: Ini tidak ada hubungannya dengan string yang mengembalikan salinan dirinya sendiri. Alasan kedua potongan kode itu berbeda ada di paragraf kedua Kevin (dijelaskan lebih lengkap dalam jawaban Squeegy).
Chuck
Tidak masalah jika Anda memiliki string atau objek dalam variabel. Anda menetapkan nilai baru yang berbeda dan kemudian variabel berisi nilai baru yang berbeda itu.
Sth
2

ini adalah versi saya dari jawabannya:

obj = {a:"hello",b:"goodbye"}
x = obj
x.a = "bonjour"

// now obj.a is equal to "bonjour"
// because x has the same reference in memory as obj
// but if I write:
x = {}
x.a = obj.a
x.b = obj.b
x.a = "bonjour"

// now x = {a:"bonjour", b:"goodbye"} and obj = {a:"hello", b:"goodbye"}
// because x points to another place in the memory
Bashir Abdelwahed
sumber
0

Anda menyetel a untuk menunjuk ke objek string baru, sementara b terus menunjuk ke objek string lama.

mdm
sumber
0

Dalam kasus pertama Anda mengubah beberapa properti objek yang terdapat dalam variabel, dalam kasus kedua Anda menetapkan nilai baru ke variabel. Itu adalah hal yang berbeda secara fundamental. Variabel adan bentah bagaimana secara ajaib ditautkan oleh tugas pertama, mereka hanya berisi objek yang sama. Itu juga terjadi pada contoh kedua, sampai Anda menetapkan nilai baru ke bvariabel.

sth
sumber
0

Perbedaannya adalah antara tipe dan objek sederhana.

Apa pun yang merupakan objek (seperti array atau fungsi) diteruskan dengan referensi.

Apa pun yang berjenis sederhana (seperti string atau angka) akan disalin.

Saya selalu memiliki fungsi copyArray yang berguna sehingga saya yakin saya tidak membuat banyak alias ke array yang sama.

Nosredna
sumber
Perbedaannya tidak terlihat dalam banyak skenario, tetapi Javascript tidak benar-benar lulus atau ditetapkan berdasarkan referensi. Ini menyalin nilai referensi.
Juan Pablo Califano
Orang-orang ini telah bekerja dengan baik dalam menjelaskannya, jadi saya hanya akan menempelkan tautan: stackoverflow.com/questions/40480/is-java-pass-by-reference (Saya mengacu pada Java, tetapi semantik untuk meneruskan dan menetapkan nilai / referensi sama seperti di Javascript)
Juan Pablo Califano
1
Sebenarnya jawaban ini salah, semuanya diteruskan oleh nilai dalam JavaScript. Dari MDN, "Parameter pemanggilan fungsi adalah argumen fungsi. Argumen diteruskan ke fungsi berdasarkan nilai. Jika fungsi mengubah nilai argumen, perubahan ini tidak tercermin secara global atau dalam fungsi pemanggil. Namun, referensi objek adalah nilai, juga, dan mereka istimewa: jika fungsi mengubah properti objek yang dirujuk, perubahan itu terlihat di luar fungsi. " developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
bittersweetryan
Primitif berperilaku seperti objek yang tidak berubah ( persis seperti mereka dalam mode ketat). Jawaban ini tidak benar.
Ry-