Apakah JavaScript merupakan bahasa referensi yang lewat atau lewat-demi-nilai?
Jenis primitif (bilangan, string, dll.) Dilewatkan oleh nilai, tetapi objek tidak diketahui, karena keduanya dapat dilewati oleh nilai (jika kita menganggap bahwa variabel yang menahan objek sebenarnya adalah referensi ke objek tersebut ) dan lulus-oleh-referensi (ketika kita menganggap bahwa variabel ke objek memegang objek itu sendiri).
Meskipun itu tidak terlalu penting pada akhirnya, saya ingin tahu bagaimana cara yang benar untuk menyajikan argumen yang melewati konvensi. Apakah ada kutipan dari spesifikasi JavaScript, yang menjelaskan semantik tentang hal ini?
Jawaban
Ini menarik di JavaScript. Pertimbangkan contoh ini:
function changeStuff(a, b, c)
{
a = a * 10;
b.item = "changed";
c = {item: "changed"};
}
var num = 10;
var obj1 = {item: "unchanged"};
var obj2 = {item: "unchanged"};
changeStuff(num, obj1, obj2);
console.log(num);
console.log(obj1.item);
console.log(obj2.item);
Ini menghasilkan keluaran:
10
changed
unchanged
- Jika
obj1
bukan referensi sama sekali, maka perubahan tidakobj1.item
akan berpengaruh diobj1
luar fungsi. - Jika argumennya menjadi rujukan yang tepat, maka semuanya akan berubah.
num
akan100
, danobj2.item
akan membaca"changed"
.
Sebaliknya, situasinya adalah bahwa item yang diteruskan diteruskan oleh nilai. Tetapi item yang dilewatkan oleh nilai itu sendiri merupakan referensi. Secara teknis, ini disebut panggilan dengan berbagi .
Secara praktis, ini berarti bahwa jika Anda mengubah parameter itu sendiri (seperti dengan num
dan obj2
), itu tidak akan memengaruhi item yang dimasukkan ke dalam parameter. Tetapi jika Anda mengubah INTERNAL dari parameter, itu akan menyebarkan kembali (seperti dengan obj1
).
Itu selalu melewati nilai, tetapi untuk objek nilai variabel adalah referensi. Karena itu, saat Anda meneruskan objek dan mengubah anggotanya , perubahan tersebut tetap ada di luar fungsi. Ini membuatnya tampak seperti referensi berlalu-lalang. Tetapi jika Anda benar-benar mengubah nilai variabel objek Anda akan melihat bahwa perubahan tidak bertahan, membuktikan itu benar-benar nilai yang lewat.
Contoh:
function changeObject(x) {
x = { member: "bar" };
console.log("in changeObject: " + x.member);
}
function changeMember(x) {
x.member = "bar";
console.log("in changeMember: " + x.member);
}
var x = { member: "foo" };
console.log("before changeObject: " + x.member);
changeObject(x);
console.log("after changeObject: " + x.member); /* change did not persist */
console.log("before changeMember: " + x.member);
changeMember(x);
console.log("after changeMember: " + x.member); /* change persists */
Keluaran:
before changeObject: foo
in changeObject: bar
after changeObject: foo
before changeMember: foo
in changeMember: bar
after changeMember: bar
Variabel tidak "menahan" objek; itu memegang referensi. Anda dapat menetapkan referensi itu ke variabel lain, dan sekarang keduanya mereferensikan objek yang sama. Itu selalu melewati nilai (bahkan ketika nilai itu adalah referensi ...).
Tidak ada cara untuk mengubah nilai yang dipegang oleh variabel yang diteruskan sebagai parameter, yang akan dimungkinkan jika JavaScript didukung lewat referensi.
Dua sen saya ... Ini adalah cara saya memahaminya. (Jangan ragu untuk mengoreksi saya jika saya salah)
Saatnya membuang semua yang Anda ketahui tentang nilai lewat / referensi.
Karena dalam JavaScript, tidak masalah apakah itu diteruskan oleh nilai atau referensi atau apapun. Yang penting adalah mutasi vs penetapan parameter yang dilewatkan ke dalam suatu fungsi.
Oke, izinkan saya melakukan yang terbaik untuk menjelaskan apa yang saya maksud. Misalkan Anda memiliki beberapa objek.
var object1 = {};
var object2 = {};
Apa yang telah kita lakukan adalah "penugasan" ... Kita telah menetapkan 2 objek kosong terpisah ke variabel "object1" dan "object2".
Sekarang, katakanlah kita lebih menyukai object1 ... Jadi, kita "menetapkan" variabel baru.
var favoriteObject = object1;
Selanjutnya, untuk alasan apa pun, kami memutuskan bahwa kami lebih menyukai objek 2. Jadi, kami melakukan sedikit penugasan ulang.
favoriteObject = object2;
Tidak ada yang terjadi pada object1 atau object2. Kami belum mengubah data apa pun. Yang kami lakukan hanyalah menetapkan kembali objek favorit kami. Penting untuk diketahui bahwa object2 dan favoriteObject sama-sama ditugaskan ke objek yang sama. Kita dapat mengubah objek itu melalui salah satu variabel tersebut.
object2.name = 'Fred';
console.log(favoriteObject.name) // Logs Fred
favoriteObject.name = 'Joe';
console.log(object2.name); // Logs Joe
Oke, sekarang mari kita lihat primitif seperti string misalnya
var string1 = 'Hello world';
var string2 = 'Goodbye world';
Sekali lagi, kami memilih favorit.
var favoriteString = string1;
Variabel favoriteString dan string1 kami ditetapkan ke 'Hello world'. Sekarang, bagaimana jika kita ingin mengubah string favorit kita ??? Apa yang akan terjadi???
favoriteString = 'Hello everyone';
console.log(favoriteString); // Logs 'Hello everyone'
console.log(string1); // Logs 'Hello world'
Uh oh .... Apa yang terjadi. Kami tidak dapat mengubah string1 dengan mengubah favoriteString ... Mengapa ?? Karena kami tidak mengubah objek string kami . Yang kami lakukan hanyalah "RE ASSIGN" variabel favoriteString ke string baru. Ini pada dasarnya memutuskannya dari string1. Pada contoh sebelumnya, saat kami mengganti nama objek kami, kami tidak menetapkan apa pun. (Yah, bukan ke variabel itu sendiri , ... kami melakukannya, namun, menetapkan properti name ke string baru.) Sebagai gantinya, kami memutasi objek yang menjaga koneksi antara 2 variabel dan objek yang mendasarinya. (Meskipun kami ingin mengubah atau mengubah objek string itu sendiri , kami tidak dapat melakukannya, karena string sebenarnya tidak dapat diubah dalam JavaScript.)
Sekarang, ke fungsi dan meneruskan parameter .... Saat Anda memanggil fungsi, dan meneruskan parameter, yang pada dasarnya Anda lakukan adalah "penugasan" ke variabel baru, dan cara kerjanya sama persis seperti jika Anda menetapkan menggunakan tanda sama (=).
Ambil contoh berikut.
var myString = 'hello';
// Assign to a new variable (just like when you pass to a function)
var param1 = myString;
param1 = 'world'; // Re assignment
console.log(myString); // Logs 'hello'
console.log(param1); // Logs 'world'
Sekarang, hal yang sama, tetapi dengan sebuah fungsi
function myFunc(param1) {
param1 = 'world';
console.log(param1); // Logs 'world'
}
var myString = 'hello';
// Calls myFunc and assigns param1 to myString just like param1 = myString
myFunc(myString);
console.log(myString); // logs 'hello'
Oke, sekarang mari kita berikan beberapa contoh menggunakan objek sebagai gantinya ... pertama, tanpa fungsi.
var myObject = {
firstName: 'Joe',
lastName: 'Smith'
};
// Assign to a new variable (just like when you pass to a function)
var otherObj = myObject;
// Let's mutate our object
otherObj.firstName = 'Sue'; // I guess Joe decided to be a girl
console.log(myObject.firstName); // Logs 'Sue'
console.log(otherObj.firstName); // Logs 'Sue'
// Now, let's reassign the variable
otherObj = {
firstName: 'Jack',
lastName: 'Frost'
};
// Now, otherObj and myObject are assigned to 2 very different objects
// And mutating one object has no influence on the other
console.log(myObject.firstName); // Logs 'Sue'
console.log(otherObj.firstName); // Logs 'Jack';
Sekarang, hal yang sama, tetapi dengan pemanggilan fungsi
function myFunc(otherObj) {
// Let's mutate our object
otherObj.firstName = 'Sue';
console.log(otherObj.firstName); // Logs 'Sue'
// Now let's re-assign
otherObj = {
firstName: 'Jack',
lastName: 'Frost'
};
console.log(otherObj.firstName); // Logs 'Jack'
// Again, otherObj and myObject are assigned to 2 very different objects
// And mutating one object doesn't magically mutate the other
}
var myObject = {
firstName: 'Joe',
lastName: 'Smith'
};
// Calls myFunc and assigns otherObj to myObject just like otherObj = myObject
myFunc(myObject);
console.log(myObject.firstName); // Logs 'Sue', just like before
Oke, jika Anda membaca seluruh posting ini, mungkin Anda sekarang memiliki pemahaman yang lebih baik tentang cara kerja pemanggilan fungsi di JavaScript. Tidak peduli apakah sesuatu diteruskan oleh referensi atau nilai ... Yang penting adalah penugasan vs mutasi.
Setiap kali Anda meneruskan variabel ke suatu fungsi, Anda "Menugaskan" apa pun nama variabel parameternya, sama seperti jika Anda menggunakan tanda sama dengan (=).
Ingatlah selalu bahwa tanda sama dengan (=) berarti tugas. Ingatlah selalu bahwa meneruskan parameter ke suatu fungsi dalam JavaScript juga berarti menetapkan. Keduanya sama dan 2 variabel dihubungkan dengan cara yang persis sama (artinya tidak, kecuali Anda menghitung bahwa variabel tersebut ditugaskan ke objek yang sama).
Satu-satunya saat "memodifikasi variabel" memengaruhi variabel yang berbeda adalah saat objek yang mendasarinya bermutasi (dalam hal ini Anda belum mengubah variabel, tetapi objek itu sendiri.
Tidak ada gunanya membuat perbedaan antara objek dan primitif, karena cara kerjanya sama persis seperti jika Anda tidak memiliki fungsi dan hanya menggunakan tanda sama dengan untuk menetapkan ke variabel baru.
Satu-satunya gotcha adalah ketika nama variabel yang Anda masukkan ke dalam fungsi sama dengan nama parameter fungsi. Ketika ini terjadi, Anda harus memperlakukan parameter di dalam fungsi seolah-olah itu adalah variabel baru yang privat untuk fungsi (karena memang demikian)
function myFunc(myString) {
// myString is private and does not affect the outer variable
myString = 'hello';
}
var myString = 'test';
myString = myString; // Does nothing, myString is still 'test';
myFunc(myString);
console.log(myString); // Logs 'test'
Pertimbangkan hal berikut:
- Variabel adalah penunjuk ke nilai-nilai dalam memori.
- Menugaskan ulang variabel hanya menunjukkan penunjuk itu ke nilai baru.
- Menugaskan ulang variabel tidak akan pernah mempengaruhi variabel lain yang menunjuk ke objek yang sama
Jadi,
lupakan tentang
"pass by reference / value"
jangan terpaku pada "pass by reference / value" karena:
- Istilah-istilah tersebut hanya digunakan untuk mendeskripsikan perilaku suatu bahasa, belum tentu implementasi yang sebenarnya mendasari. Sebagai hasil dari abstraksi ini, detail penting yang penting untuk penjelasan yang layak hilang, yang pasti mengarah ke situasi saat ini di mana satu istilah tidak cukup menggambarkan perilaku sebenarnya dan info tambahan harus disediakan.
- Konsep-konsep ini awalnya tidak didefinisikan dengan maksud untuk mendeskripsikan javascript secara khusus dan jadi saya tidak merasa harus menggunakannya ketika mereka hanya menambah kebingungan.
Untuk menjawab pertanyaan Anda: petunjuk diberikan.
// code
var obj = {
name: 'Fred',
num: 1
};
// illustration
'Fred'
/
/
(obj) ---- {}
\
\
1
// code
obj.name = 'George';
// illustration
'Fred'
(obj) ---- {} ----- 'George'
\
\
1
// code
obj = {};
// illustration
'Fred'
(obj) {} ----- 'George'
| \
| \
{ } 1
// code
var obj = {
text: 'Hello world!'
};
/* function parameters get their own pointer to
* the arguments that are passed in, just like any other variable */
someFunc(obj);
// illustration
(caller scope) (someFunc scope)
\ /
\ /
\ /
\ /
\ /
{ }
|
|
|
'Hello world'
Beberapa komentar terakhir:
- Sangat menggoda untuk berpikir bahwa primitif ditegakkan oleh aturan khusus sementara objek tidak, tetapi primitif hanyalah akhir dari rantai penunjuk.
- Sebagai contoh terakhir, pertimbangkan mengapa upaya umum untuk menghapus array tidak berfungsi seperti yang diharapkan.
var a = [1,2];
var b = a;
a = [];
console.log(b); // [1,2]
// doesn't work because `b` is still pointing at the original array
Objek di luar fungsi diteruskan menjadi fungsi dengan memberikan referensi ke objek luar.
Saat Anda menggunakan referensi tersebut untuk memanipulasi objeknya, objek di luar akan terpengaruh. Namun, jika di dalam fungsi Anda memutuskan untuk mengarahkan referensi ke sesuatu yang lain, Anda sama sekali tidak memengaruhi objek di luar, karena yang Anda lakukan hanyalah mengarahkan referensi ke hal lain.
Pikirkan seperti ini: Itu selalu melewati nilai. Akan tetapi, nilai suatu objek bukanlah objek itu sendiri, tetapi referensi ke objek itu.
Berikut adalah contoh, melewatkan angka (tipe primitif)
function changePrimitive(val) {
// At this point there are two '10's in memory.
// Changing one won't affect the other
val = val * 10;
}
var x = 10;
changePrimitive(x);
// x === 10
Mengulangi ini dengan suatu objek menghasilkan hasil yang berbeda:
function changeObject(obj) {
// At this point there are two references (x and obj) in memory,
// but these both point to the same object.
// changing the object will change the underlying object that
// x and obj both hold a reference to.
obj.val = obj.val * 10;
}
var x = { val: 10 };
changeObject(x);
// x === { val: 100 }
Satu contoh lagi:
function changeObject(obj) {
// Again there are two references (x and obj) in memory,
// these both point to the same object.
// now we create a completely new object and assign it.
// obj's reference now points to the new object.
// x's reference doesn't change.
obj = { val: 100 };
}
var x = { val: 10 };
changeObject(x);
// x === { val: 10}
Penjelasan yang sangat rinci tentang menyalin, meneruskan, dan membandingkan menurut nilai dan dengan referensi ada di bab buku "JavaScript: Panduan Definitif" ini.
Sebelum kita meninggalkan topik memanipulasi objek dan array dengan referensi, kita perlu menjernihkan titik nomenklatur.
Frasa "lewat referensi" dapat memiliki beberapa arti. Bagi beberapa pembaca, frasa tersebut merujuk pada teknik pemanggilan fungsi yang memungkinkan fungsi untuk menetapkan nilai baru ke argumennya dan agar nilai yang dimodifikasi tersebut terlihat di luar fungsi. Istilah ini tidak digunakan dalam buku ini.
Di sini, yang kami maksud sederhana adalah referensi ke objek atau larik - bukan objek itu sendiri - diteruskan ke suatu fungsi. Sebuah fungsi dapat menggunakan referensi untuk mengubah properti objek atau elemen array. Tetapi jika fungsi menimpa referensi dengan referensi ke objek atau larik baru, modifikasi itu tidak terlihat di luar fungsi.
Pembaca yang akrab dengan arti lain dari istilah ini mungkin lebih suka mengatakan bahwa objek dan array dilewatkan oleh nilai, tetapi nilai yang diteruskan sebenarnya adalah referensi daripada objek itu sendiri.
JavaScript selalu nilai-demi-nilai ; semuanya berjenis nilai.
Objek adalah nilai, dan fungsi anggota objek adalah nilai itu sendiri (ingat bahwa fungsi adalah objek kelas satu di JavaScript). Juga, berkenaan dengan konsep bahwa segala sesuatu di JavaScript adalah sebuah objek ; ini salah. String, simbol, angka, boolean, null, dan undefined adalah primitif .
Terkadang mereka dapat memanfaatkan beberapa fungsi dan properti anggota yang diwarisi dari prototipe dasar mereka, tetapi ini hanya untuk kenyamanan. Ini tidak berarti bahwa mereka adalah objek itu sendiri. Coba yang berikut ini untuk referensi:
x = "test";
alert(x.foo);
x.foo = 12;
alert(x.foo);
Di kedua lansiran, Anda akan menemukan nilainya tidak ditentukan.
Dalam JavaScript, jenis nilai hanya mengontrol apakah nilai itu akan diberikan oleh nilai-copy atau dengan referensi-salinan .
Nilai primitif selalu ditetapkan / diteruskan oleh value-copy :
null
undefined
- tali
- jumlah
- boolean
- simbol dalam
ES6
Nilai gabungan selalu ditetapkan / diteruskan oleh salinan referensi
- benda
- array
- fungsi
Sebagai contoh
var a = 2;
var b = a; // `b` is always a copy of the value in `a`
b++;
a; // 2
b; // 3
var c = [1,2,3];
var d = c; // `d` is a reference to the shared `[1,2,3]` value
d.push( 4 );
c; // [1,2,3,4]
d; // [1,2,3,4]
Dalam cuplikan di atas, karena 2
merupakan primitif skalar, a
menyimpan satu salinan awal dari nilai itu, dan b
diberi salinan nilai lainnya. Saat berubah b
, Anda sama sekali tidak mengubah nilainya a
.
Tetapi keduanya c
dan d
merupakan referensi terpisah ke nilai bersama yang sama [1,2,3]
, yang merupakan nilai majemuk. Sangat penting untuk dicatat bahwa baik c
atau d
lebih "memiliki" [1,2,3]
nilai - keduanya hanya sama referensi peer to nilai. Jadi, saat menggunakan salah satu referensi untuk memodifikasi ( .push(4)
) nilai bersama yang sebenarnya array
, itu hanya memengaruhi satu nilai bersama, dan kedua referensi akan merujuk nilai yang baru diubah [1,2,3,4]
.
var a = [1,2,3];
var b = a;
a; // [1,2,3]
b; // [1,2,3]
// later
b = [4,5,6];
a; // [1,2,3]
b; // [4,5,6]
Saat kami membuat tugas b = [4,5,6]
, kami sama sekali tidak melakukan apa pun untuk memengaruhi where a
is still referencing ( [1,2,3]
). Untuk melakukan itu, b
harus menjadi penunjuk a
daripada referensi ke array
- tetapi tidak ada kemampuan seperti itu di JS!
function foo(x) {
x.push( 4 );
x; // [1,2,3,4]
// later
x = [4,5,6];
x.push( 7 );
x; // [4,5,6,7]
}
var a = [1,2,3];
foo( a );
a; // [1,2,3,4] not [4,5,6,7]
Ketika kita meneruskan argumen a
, itu memberikan salinan a
referensi ke x
. x
dan a
merupakan referensi terpisah yang menunjuk pada nilai yang sama [1,2,3]
. Sekarang, di dalam fungsi, kita bisa menggunakan referensi itu untuk mengubah nilainya sendiri ( push(4)
). Tapi ketika kita membuat penugasan x = [4,5,6]
, ini sama sekali tidak mempengaruhi ke mana referensi awal a
menunjuk - masih menunjuk pada nilai (sekarang diubah) [1,2,3,4]
.
Untuk meneruskan nilai majemuk (seperti an array
) secara efektif dengan value-copy, Anda perlu membuat salinannya secara manual, sehingga referensi yang diteruskan tidak masih mengarah ke aslinya. Sebagai contoh:
foo( a.slice() );
Nilai gabungan (objek, larik, dll) yang dapat dilewatkan oleh salinan referensi
function foo(wrapper) {
wrapper.a = 42;
}
var obj = {
a: 2
};
foo( obj );
obj.a; // 42
Di sini, obj
bertindak sebagai pembungkus untuk properti primitif skalar a
. Saat diteruskan ke foo(..)
, salinan obj
referensi diteruskan dan disetel ke wrapper
parameter. Sekarang kita dapat menggunakan wrapper
referensi untuk mengakses objek bersama, dan memperbarui propertinya. Setelah fungsi selesai, obj.a
akan terlihat nilai yang diperbarui 42
.
Sumber
baik, ini tentang 'kinerja' dan 'kecepatan' dan dalam kata sederhana 'manajemen memori' dalam bahasa pemrograman.
di javascript kita dapat meletakkan nilai dalam dua lapisan: type1 - objects
dan type2 -semua jenis nilai lainnya seperti string
& boolean
& etc
jika Anda membayangkan memori seperti kotak di bawah ini yang di setiap kotak hanya satu nilai tipe2 yang dapat disimpan:
setiap nilai tipe2 (hijau) adalah kotak tunggal sedangkan nilai tipe1 (biru) adalah kelompoknya :
intinya adalah jika Anda ingin menunjukkan nilai tipe2, alamatnya sederhana tetapi jika Anda ingin melakukan hal yang sama untuk nilai tipe1, itu tidak mudah sama sekali! :
dan dalam cerita yang lebih rumit:
jadi di sini referensi dapat menyelamatkan kita:
sedangkan panah hijau di sini adalah variabel tipikal, yang ungu adalah variabel objek, jadi karena panah hijau (variabel tipikal) hanya memiliki satu tugas (dan itu menunjukkan nilai tipikal) kita tidak perlu memisahkan nilainya dari itu jadi kami memindahkan panah hijau dengan nilai itu kemanapun itu pergi dan di semua tugas, fungsi dan sebagainya ...
tapi kita tidak bisa melakukan hal yang sama dengan panah ungu, kita mungkin ingin memindahkan sel 'john' ke sini atau banyak hal lainnya ..., jadi panah ungu akan menempel pada tempatnya dan hanya panah biasa yang ditugaskan padanya akan bergerak ...
situasi yang sangat membingungkan adalah ketika Anda tidak dapat menyadari bagaimana variabel referensi Anda berubah, mari kita lihat contoh yang sangat bagus:
let arr = [1, 2, 3, 4, 5]; //arr is an object now and a purple arrow is indicating it
let obj2 = arr; // now, obj2 is another purple arrow that is indicating the value of arr obj
let obj3 = ['a', 'b', 'c'];
obj2.push(6); // first pic below - making a new hand for the blue circle to point the 6
//obj2 = [1, 2, 3, 4, 5, 6]
//arr = [1, 2, 3, 4, 5, 6]
//we changed the blue circle object value (type1-value) and due to arr and obj2 are indicating that so both of them changed
obj2 = obj3; //next pic below - changing the direction of obj2 array from blue circle to orange circle so obj2 is no more [1,2,3,4,5,6] and it's no more about changing anything in it but we completely changed its direction and now obj2 is pointing to obj3
//obj2 = ['a', 'b', 'c'];
//obj3 = ['a', 'b', 'c'];
Ini sedikit penjelasan lebih untuk pass by value dan pass by reference (JavaScript). Dalam konsep ini, mereka berbicara tentang melewatkan variabel dengan referensi dan meneruskan variabel dengan referensi.
Lewati nilai (tipe primitif)
var a = 3;
var b = a;
console.log(a); // a = 3
console.log(b); // b = 3
a=4;
console.log(a); // a = 4
console.log(b); // b = 3
- berlaku untuk semua tipe primitif dalam JavaScript (string, number, Boolean, undefined, dan null).
- a dialokasikan memori (katakanlah 0x001) dan b membuat salinan nilai dalam memori (katakanlah 0x002).
- Jadi mengubah nilai variabel tidak memengaruhi yang lain, karena keduanya berada di dua lokasi berbeda.
Lewati referensi (objek)
var c = { "name" : "john" };
var d = c;
console.log(c); // { "name" : "john" }
console.log(d); // { "name" : "john" }
c.name = "doe";
console.log(c); // { "name" : "doe" }
console.log(d); // { "name" : "doe" }
- Mesin JavaScript menetapkan objek ke variabel
c
, dan itu menunjuk ke beberapa memori, katakanlah (0x012). - Jika d = c, langkah ini
d
menunjuk ke lokasi yang sama (0x012). - Mengubah nilai setiap perubahan nilai untuk kedua variabel.
- Fungsi adalah objek
Kasus khusus, melewati referensi (objek)
c = {"name" : "jane"};
console.log(c); // { "name" : "jane" }
console.log(d); // { "name" : "doe" }
- The equal(=) operator sets up new memory space or address
sharing what I know of references in JavaScript
In JavaScript, when assigning an object to a variable, the value assigned to the variable is a reference to the object:
var a = {
a: 1,
b: 2,
c: 3
};
var b = a;
// b.c is referencing to a.c value
console.log(b.c) // Output: 3
// Changing value of b.c
b.c = 4
// Also changes the value of a.c
console.log(a.c) // Output: 4
Semantics!! Setting concrete definitions will necessarily make some answers and comments incompatible since they are not describing the same thing even when using the same words and phrases, but it is critical to get past the confusion (especially for new programmers).
First of all, there are multiple levels of abstraction that not everyone seems to grasp. Newer programmers who have learned on 4th or 5th generation languages may have difficulty wrapping their mind around concepts familiar to assembly or C programmers not phased by pointers to pointers to pointers. Pass-by-reference does not simply mean the ability to change a referenced object using a function parameter variable.
Variable: Combined concept of a symbol which references a value at a particular location in memory. This term is usually too loaded to be used alone in discussing details.
Symbol: Text string used to refer to variable (i.e. variable's name).
Value: Particular bits stored in memory and referenced using variable's symbol.
Memory location: Where a variable's value is stored. (The location itself is represented by a number separate from the value stored at the location.)
Function parameter: Variable declared in a function definition, used for referencing variables passed to the function.
Function argument: Variable outside the function which is passed to the function by the caller.
Object variable: Variable whose basic underlying value is not the "object" itself, rather its value is a pointer (memory location value) to another location in memory where the object's actual data is stored. In most higher-generation languages, the "pointer" aspect is effectively hidden by automatic de-referencing in various contexts.
Primitive variable: Variable whose value IS the actual value. Even this concept can be complicated by auto-boxing and object-like contexts of various languages, but the general ideas is that the variable's value IS the actual value represented by the variable's symbol rather than a pointer to another memory location.
Function arguments and parameters are not the same thing. Also, a variable's value is not the variable's object (as already pointed out by various people, but apparently ignored). These distinctions are critical to proper understanding.
Pass-by-value or Call-by-sharing (for objects): The function argument's value is COPIED to another memory location which is referenced by the function's parameter symbol (regardless of whether it's on the stack or heap). In other words, the function parameter received a copy of the passed argument's value... AND (critical) the argument's value IS NEVER UPDATED / ALTERED / CHANGED by the calling function. Remember, an object variable's value is NOT the object itself, rather it is the pointer to the object, so passing an object variable by value copies the pointer to the function parameter variable. The function parameter's value points to the exact same object in memory. The object data itself can be altered directly via the function parameter, BUT the function argument's value IS NEVER UPDATED, so it will continue to point to the same object throughout and even after the function call (even if its object's data was altered or if the function parameter is assigned a different object altogether). It is incorrect to conclude that the function argument was passed by reference just because the referenced object is updatable via the function parameter variable.
Call / Pass-by-reference: The function argument's value can/will be updated directly by the corresponding function parameter. If it helps, the function parameter becomes an effective "alias" for the argument--they effectively refer to the same value at the same memory location. If a function argument is an object variable, the ability to change the object's data is no different than the pass-by-value case since the function parameter will still point to the same object as the argument. But in the object variable case, if the function parameter is set to a completely different object, then the argument will likewise also point to the different object--this does not happen in the pass-by-value case.
JavaScript does not pass by reference. If you read closely, you will realize that all contrary opinions misunderstand what is meant by pass-by-value and they falsely conclude that the ability to update an object's data via the function parameter is synonymous to "pass-by-value".
Object clone/copy: A new object is created and the original object's data is copied. This can be a deep copy or shallow copy, but the point is that a new object is created. Creating a copy of an object is a separate concept from pass-by-value. Some languages distinguish between class object and structs (or the like), and may have different behavior for passing variables of the different types. But JavaScript does not do anything like this automatically when passing object variables. But the absence of automatic object cloning does not translate to pass-by-reference.
JavaScript passes primitive types by value and object types by reference
Now, people like to bicker endlessly about whether "pass by reference" is the correct way to describe what Java et al. actually do. The point is this:
- Passing an object does not copy the object.
- An object passed to a function can have its members modified by the function.
- A primitive value passed to a function cannot be modified by the function. A copy is made.
In my book that's called passing by reference.
— Brian Bi - Which programming languages are pass by reference?
Update
Here is a rebuttal to this:
There is no "pass by reference" available in JavaScript.
My simple way to understand this...
When calling a function, you are passing the content (reference or value) of the argument variables, not the the variables themselves.
var var1 = 13; var var2 = { prop: 2 }; //13 and var2's content (reference) are being passed here foo(var1, var2);
Inside the function, parameter variables,
inVar1
andinVar2
, receive the contents being passed.function foo(inVar1, inVar2){ //changing contents of inVar1 and inVar2 won't affect variables outside inVar1 = 20; inVar2 = { prop: 7 }; }
Since
inVar2
received the reference of{ prop: 2 }
, you can change the value of the object's property.function foo(inVar1, inVar2){ inVar2.prop = 7; }
Passing arguments to a function in JavaScript is analogous to passing parameters by pointer value in C:
/*
The following C program demonstrates how arguments
to JavaScript functions are passed in a way analogous
to pass-by-pointer-value in C. The original JavaScript
test case by @Shog9 follows with the translation of
the code into C. This should make things clear to
those transitioning from C to JavaScript.
function changeStuff(num, obj1, obj2)
{
num = num * 10;
obj1.item = "changed";
obj2 = {item: "changed"};
}
var num = 10;
var obj1 = {item: "unchanged"};
var obj2 = {item: "unchanged"};
changeStuff(num, obj1, obj2);
console.log(num);
console.log(obj1.item);
console.log(obj2.item);
This produces the output:
10
changed
unchanged
*/
#include <stdio.h>
#include <stdlib.h>
struct obj {
char *item;
};
void changeStuff(int *num, struct obj *obj1, struct obj *obj2)
{
// make pointer point to a new memory location
// holding the new integer value
int *old_num = num;
num = malloc(sizeof(int));
*num = *old_num * 10;
// make property of structure pointed to by pointer
// point to the new value
obj1->item = "changed";
// make pointer point to a new memory location
// holding the new structure value
obj2 = malloc(sizeof(struct obj));
obj2->item = "changed";
free(num); // end of scope
free(obj2); // end of scope
}
int num = 10;
struct obj obj1 = { "unchanged" };
struct obj obj2 = { "unchanged" };
int main()
{
// pass pointers by value: the pointers
// will be copied into the argument list
// of the called function and the copied
// pointers will point to the same values
// as the original pointers
changeStuff(&num, &obj1, &obj2);
printf("%d\n", num);
puts(obj1.item);
puts(obj2.item);
return 0;
}
For programming language lawyers, I've went through the following sections of ECMAScript 5.1 (which is easier to read than the latest edition), and go as far as asking it on the ECMAScript mailing list.
TL;DR: Everythings're passed by value, but properties of Objects are references, and the definition of Object is creepily lacking in the standard.
Construction of Argument Lists
Section 11.2.4 "Argument Lists" says the following on producing a argument list consisting of only 1 argument:
The production ArgumentList : AssignmentExpression is evaluated as follows:
- Let ref be the result of evaluating AssignmentExpression.
- Let arg be GetValue(ref).
- Return a List whose sole item is arg.
The section also enumerate cases where argument list has 0 or >1 arguments.
Thus, everything's are passed by reference.
Access of Object Properties
Section 11.2.1 "Property Accessors"
The production MemberExpression : MemberExpression [ Expression ] is evaluated as follows:
- Let baseReference be the result of evaluating MemberExpression.
- Let baseValue be GetValue(baseReference).
- Let propertyNameReference be the result of evaluating Expression.
- Let propertyNameValue be GetValue(propertyNameReference).
- Call CheckObjectCoercible(baseValue).
- Let propertyNameString be ToString(propertyNameValue).
- If the syntactic production that is being evaluated is contained in strict mode code, let strict be true, else let strict be false.
- Return a value of type Reference whose base value is baseValue and whose referenced name is propertyNameString, and whose strict mode flag is strict.
Thus, properties of Objects are always available as reference.
On Reference
It is described in section 8.7 "The Reference Specification Type", that references are not real types in the language - they're only used to describe the behavior of the delete, the typeof, and the assignment operators.
Definition of "Object"
It is defined in 5.1 edition that "An Object is a collection of properties". Therefore, we can infer, that the value of the object is the collection, but as to what is the value of the collection is poorly defined in the spec, and requires a bit of effort to understand.
The MDN docs explain it clearly, without being too verbose:
The parameters of a function call are the function's arguments. Arguments are passed to functions by value. If the function changes the value of an argument, this change is not reflected globally or in the calling function. However, object references are values, too, and they are special: if the function changes the referred object's properties, that change is visible outside the function, (...)
Source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions#Description
observation: If there is no way for an observer to examine the underlying memory of the engine, there is no way to determine wether an immutable value gets copied or a reference gets passed.
JavaScript is more or less agnostic to the underlying memory model. There is no such thing as a reference². JavaScript has values. Two variables can hold the same value (or more accurate: two environment records can bind the same value). The only type of values that can be mutated are objects through their abstract [[Get]] and [[Set]] operations. If you forget about computers and memory, this is all you need to describe JavaScripts behaviour, and it allows you to understand the specification.
let a = { prop: 1 };
let b = a; // a and b hold the same value
a.prop = "test"; // the object gets mutated, can be observed through both a and b
b = { prop: 2 }; // b holds now a different value
Now you might ask yourself how two variables can hold the same value on a computer. You might then look into the sourcecode of a JavaScript engine and you'll most likely find something which a programmer of the language the engine was written in would call a reference.
So in fact you can say that JavaScript is "pass by value", whereas the value can be shared, you can say that JavaScript is "pass by reference", which might be a useful logical abstraction for programmers from low level languages, or you might call the behaviour "call by sharing". As there is no such thing as a reference in JavaScript, all of these are neither wrong nor on point. Therefore I don't think the answer is particularily useful to search for.
² The term Reference in the specification is not a reference in the traditional sense. It is a container for an object and the name of a property, and is an intermediate value (e.g. a.b
evaluates to Reference { value = a, name = "b" }
). The term reference also sometimes appears in the specification in unrelated sections.
The most succinct explanation I found was in the AirBNB style guide:
Primitives: When you access a primitive type you work directly on its value
- string
- number
- boolean
- null
- undefined
E.g.:
var foo = 1,
bar = foo;
bar = 9;
console.log(foo, bar); // => 1, 9
Complex: When you access a complex type you work on a reference to its value
- object
- array
- function
E.g.:
var foo = [1, 2],
bar = foo;
bar[0] = 9;
console.log(foo[0], bar[0]); // => 9, 9
I.e. effectively primitive types are passed by value, and complex types are passed by reference.
I've read through these answers multiple times, but didn't REALLY get it until I learned about the technical definition of "Call by sharing" as termed by Barbara Liskov
The semantics of call by sharing differ from call by reference in that assignments to function arguments within the function aren't visible to the caller (unlike by reference semantics)[citation needed], so e.g. if a variable was passed, it is not possible to simulate an assignment on that variable in the caller's scope. However, since the function has access to the same object as the caller (no copy is made), mutations to those objects, if the objects are mutable, within the function are visible to the caller, which may appear to differ from call by value semantics. Mutations of a mutable object within the function are visible to the caller because the object is not copied or cloned — it is shared.
That is, parameter references are alterable if you go and access the parameter value itself. On the other hand, assignment to a parameter will disappear after evaluation, and is non-accessible to the function caller.
In a low-level language, if you want to pass a variable by reference, you have to use a specific syntax in the creation of the function:
int myAge = 14;
increaseAgeByRef(myAge);
function increaseAgeByRef(int &age) {
*age = *age + 1;
}
The &age
is a reference to myAge
, but if you want the value you have to convert the reference, using *age
.
JavaScript is a high level language that does this conversion for you.
So, although objects are passed by reference, the language converts the reference parameter to the value. You don't need to use &
, on the function definition, to pass it by reference, neither *
, on the function body, to convert the reference to the value, JavaScript does it for you.
That's why when you try to change an object inside a function, by replacing it's value (i.e. age = {value:5}
), the change doesn't persist, but if you change it's properties (i.e. age.value = 5
), it does.
Learn more