Apakah JavaScript adalah pass-by-reference atau pass-by-value language?


1405

Tipe-tipe primitif (angka, string, dll.) Dilewatkan oleh nilai, tetapi objek tidak diketahui, karena keduanya dapat lewat-oleh-nilai (dalam kasus kami menganggap bahwa variabel yang memegang objek sebenarnya adalah referensi ke objek ) dan lulus-oleh-referensi (ketika kita menganggap bahwa variabel ke objek memegang objek itu sendiri).

Meskipun pada akhirnya tidak terlalu penting, saya ingin tahu apa cara yang benar untuk menyajikan argumen yang lolos dari konvensi. Apakah ada kutipan dari spesifikasi JavaScript, yang mendefinisikan apa yang seharusnya menjadi semantik mengenai ini?


2
Saya pikir Anda secara tidak sengaja membalik definisi pass-by-value dan pass-by-reference ... "pass-by-value (jika kami menganggap bahwa variabel yang memegang objek sebenarnya adalah referensi ke objek) dan lulus -dengan referensi (ketika kita menganggap bahwa variabel ke objek memegang objek itu sendiri) "
Niko Bellic

5
Iya. Terlepas dari sintaksisnya, dalam pemanggilan fungsi apa pun dalam bahasa pemrograman apa pun, pass-by-reference berarti data yang terkait dengan variabel yang diteruskan tidak disalin ketika diteruskan ke fungsi, dan dengan demikian setiap modifikasi yang dibuat oleh fungsi ke variabel yang dilewati akan dipertahankan. dalam program setelah panggilan fungsi berakhir. Pass-by-value berarti data yang terkait dengan variabel tersebut benar-benar disalin ketika diteruskan ke fungsi dan setiap modifikasi yang dibuat oleh fungsi tersebut ke variabel tersebut akan hilang ketika variabel keluar dari ruang lingkup fungsi fungsi ketika fungsi kembali.
John Sonderson

5
Pertanyaan lama ini agak beracun karena jawabannya yang banyak dipilih salah. JavaScript benar - benar merupakan nilai tambah .
Runcing

6
@DanailNachev Terminologi ini sangat membingungkan. Masalahnya adalah, "pass by value" dan "pass by reference" adalah istilah yang ada sebelum banyak fitur bahasa pemrograman modern. Kata-kata "nilai" dan "referensi" merujuk secara khusus ke parameter seperti yang muncul dalam ekspresi panggilan fungsi. JavaScript selalu mengevaluasi setiap ekspresi dalam daftar parameter panggilan fungsi sebelum memanggil fungsi, sehingga parameter selalu bernilai. Bagian yang membingungkan adalah bahwa referensi ke objek adalah nilai-nilai JavaScript yang umum. Namun, itu tidak menjadikannya bahasa "lewat referensi".
Runcing

2
@DanailNachev "lulus dengan referensi" secara khusus berarti bahwa jika Anda memiliki var x=3, y=x; f(x); alert(y === x);fungsi f()dapat membuat laporan peringatan falsedan tidak true. Dalam JavaScript, itu tidak mungkin, jadi itu bukan pass-by-reference. Adalah baik bahwa mungkin untuk meneruskan referensi ke objek yang dapat dimodifikasi, tetapi bukan itu yang dimaksud dengan "lulus dengan referensi". Seperti yang saya katakan, sangat disayangkan bahwa terminologinya sangat membingungkan.
Runcing

Jawaban:


1599

Sangat menarik dalam 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 output:

10
changed
unchanged
  • Jika obj1bukan referensi sama sekali, maka perubahan tidak obj1.itemakan berpengaruh pada bagian obj1luar fungsi.
  • Jika argumen itu adalah referensi yang tepat, maka semuanya akan berubah. numakan 100, dan obj2.itemakan membaca "changed".

Alih-alih, situasinya adalah bahwa item yang diteruskan dilewatkan dengan nilai. Tetapi item yang dilewatkan oleh nilai itu sendiri adalah referensi. Secara teknis, ini disebut call-by-sharing .

Dalam istilah praktis, ini berarti bahwa jika Anda mengubah parameter itu sendiri (seperti dengan numdan obj2), itu tidak akan memengaruhi item yang dimasukkan ke dalam parameter. Tetapi jika Anda mengubah INTERNAL parameter, itu akan menyebar kembali (seperti halnya obj1).


32
Ini persis sama (atau setidaknya secara semantik) dengan C #. Objek memiliki dua jenis: Nilai (tipe primitif) dan Referensi.
Peter Lee

53
Saya pikir ini juga digunakan di Java: reference-by-value.
Jpnh

296
alasan sebenarnya adalah bahwa di dalam changeStuff, num, obj1, dan obj2 adalah referensi. Saat Anda mengubah itemproperti objek yang dirujuk oleh obj1, Anda mengubah nilai properti item yang awalnya disetel ke "tidak berubah". Ketika Anda memberikan obj2 nilai {item: "berubah"} Anda mengubah referensi ke objek baru (yang segera keluar dari ruang lingkup ketika fungsi keluar). Menjadi lebih jelas apa yang terjadi jika Anda menamai fungsi params hal-hal seperti numf, obj1f, dan obj2f. Kemudian Anda melihat bahwa params menyembunyikan nama var eksternal.
jinglesthula

13
@BartoNaz Tidak juga. Yang Anda inginkan adalah melewati referensi dengan referensi, alih-alih meneruskan referensi dengan nilai. Tetapi JavaScript selalu melewati referensi berdasarkan nilai, sama seperti JavaScript melewati semua nilai lainnya. (Sebagai perbandingan, C # memiliki perilaku pass-reference-by-value mirip dengan JavaScript dan Java, tetapi memungkinkan Anda menentukan pass-reference-by-reference dengan refkata kunci.) Biasanya Anda hanya akan memiliki fungsi mengembalikan objek baru, dan melakukan tugas pada titik di mana Anda memanggil fungsi. Misalnya, foo = GetNewFoo();alih-alihGetNewFoo(foo);
Tim Goodman

56
Meskipun jawaban ini adalah yang paling populer, ini bisa sedikit membingungkan karena menyatakan "Jika itu murni lewat nilai". JavaScript adalah murni pass-by-value. Tetapi nilai yang dilewatkan adalah referensi. Ini tidak dibatasi untuk melewati parameter sama sekali. Anda cukup menyalin variabel dengan var obj1 = { item: 'unchanged' }; var obj2 = obj1; obj2.item = 'changed';dan akan mengamati efek yang sama seperti pada contoh Anda. Karena itu saya pribadi merujuk jawaban Tim Goodman
chiccodoro

476

Itu selalu melewati nilai, tetapi untuk objek nilai variabel adalah referensi. Karena itu, ketika Anda melewati objek dan mengubah anggotanya , perubahan itu tetap ada di luar fungsi. Ini membuatnya terlihat seperti referensi lewat. Tetapi jika Anda benar-benar mengubah nilai variabel objek Anda akan melihat bahwa perubahan itu tidak bertahan, membuktikan itu benar-benar melewati nilai.

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

14
@ daylight: Sebenarnya, Anda salah; jika disahkan oleh const ref mencoba melakukan changeObject akan menyebabkan kesalahan, bukan hanya gagal. Coba tetapkan nilai baru ke referensi const di C ++ dan kompiler menolaknya. Dalam istilah pengguna, itulah perbedaan antara pass by value dan pass by const reference.
deworde

5
@ daylight: Ini bukan referensi konstan. Di changeObject, saya telah berubah xuntuk berisi referensi ke objek baru. x = {member:"bar"};setara dengan x = new Object(); x.member = "bar"; Apa yang saya katakan juga berlaku untuk C #, omong-omong.
Tim Goodman

2
@ daylight: Untuk C #, Anda dapat melihat ini dari luar fungsi, jika Anda menggunakan refkata kunci, Anda dapat melewati referensi dengan referensi (alih-alih default melewati referensi dengan nilai), dan kemudian perubahan ke menunjuk ke new Object() akan tetap ada .
Tim Goodman

11
@adityamenon Sulit untuk menjawab "mengapa", tetapi saya akan mencatat bahwa perancang Jawa dan C # membuat pilihan yang sama; ini bukan hanya keanehan JavaScript. Sungguh, itu sangat konsisten nilai-demi-nilai, hal yang membuatnya membingungkan bagi orang-orang adalah bahwa nilai dapat menjadi referensi. Ini tidak jauh berbeda dari melewatkan pointer di sekitar (berdasarkan nilai) dalam C ++ dan kemudian melakukan dereferencing untuk mengatur anggota. Tidak ada yang akan terkejut bahwa perubahan itu berlanjut. Tetapi karena bahasa-bahasa ini mengabstraksikan penunjuk dan diam-diam melakukan dereferensi untuk Anda, orang jadi bingung.
Tim Goodman

41
Dengan kata lain, hal yang membingungkan di sini bukanlah pass-by-value / pass-by-reference. Semuanya adalah nilai demi nilai, perhentian penuh. Yang membingungkan adalah bahwa Anda tidak bisa melewatkan objek, Anda juga tidak bisa menyimpan objek dalam variabel. Setiap kali Anda berpikir Anda melakukan itu, Anda benar - benar melewati atau menyimpan referensi ke objek itu. Tetapi ketika Anda pergi untuk mengakses anggotanya, ada diam-diam dereferencing yang terjadi, yang mengabadikan fiksi bahwa variabel Anda memegang objek yang sebenarnya.
Tim Goodman

150

Variabel tidak "menahan" objek; itu memegang referensi. Anda dapat menetapkan referensi itu ke variabel lain, dan sekarang keduanya merujuk objek yang sama. Itu selalu melewati nilai (bahkan ketika nilai itu adalah referensi ...).

Tidak ada cara untuk mengubah nilai yang dimiliki oleh variabel yang diteruskan sebagai parameter, yang akan mungkin jika JavaScript didukung lewat referensi.


2
Ini sedikit membingungkan saya. Tidak lulus referensi demi referensi?

8
Penulis berarti bahwa dengan melewatkan referensi, Anda memberikan nilai referensi (cara lain untuk memikirkannya adalah dengan melewatkan nilai dari alamat memori). Jadi itu sebabnya jika Anda mendeklarasikan ulang objek, dokumen asli tidak berubah, karena Anda membuat objek baru di lokasi memori yang berbeda. Jika Anda mengubah properti, objek asli berubah karena Anda mengubahnya di lokasi memori asli (yang tidak dipindahkan).
Huy-Anh Hoang

113

Dua sen saya ... Ini adalah cara saya memahaminya. (Jangan ragu untuk mengoreksi saya jika saya salah)

Saatnya membuang semua yang Anda ketahui tentang nilai / referensi.

Karena dalam JavaScript, tidak masalah apakah itu diteruskan oleh nilai atau referensi atau apa pun. Yang penting adalah mutasi vs penugasan parameter dilewatkan ke fungsi.

OK, biarkan saya melakukan yang terbaik untuk menjelaskan apa yang saya maksud. Katakanlah Anda memiliki beberapa objek.

var object1 = {};
var object2 = {};

Apa yang telah kami lakukan adalah "tugas" ... Kami telah menetapkan 2 objek kosong terpisah ke variabel "object1" dan "object2".

Sekarang, katakanlah kita menyukai objek1 lebih baik ... Jadi, kita "menetapkan" variabel baru.

var favoriteObject = object1;

Selanjutnya, untuk alasan apa pun, kami memutuskan bahwa kami lebih suka objek 2. Jadi, kami hanya 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 ulang apa objek favorit kami. Penting untuk mengetahui bahwa object2 dan favoriteObject keduanya ditugaskan untuk 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

OK, 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 ditugaskan ke 'Hello world'. Sekarang, bagaimana jika kita ingin mengubah Stringsing 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 mengubahString favorit ... Mengapa ?? Karena kami tidak mengubah objek string kami . Yang kami lakukan adalah "RE ASSIGN" variabel favoriteString ke string baru. Ini pada dasarnya memutusnya dari string1. Pada contoh sebelumnya, ketika kami mengganti nama objek kami, kami tidak menetapkan apa pun. (Yah, bukan ke variabel itu sendiri , ... kami, bagaimanapun, menetapkan properti nama ke string baru.) Sebaliknya, kami hanya bermutasi objek yang menjaga koneksi antara 2 variabel dan objek yang mendasarinya. (Bahkan jika kita ingin memodifikasi atau mengubah objek string itu sendiri, kami tidak dapat melakukannya, karena string sebenarnya tidak dapat diubah dalam JavaScript.)

Sekarang, ke fungsi dan melewati parameter .... Ketika Anda memanggil fungsi, dan melewatkan parameter, apa yang Anda lakukan pada dasarnya adalah "tugas" ke variabel baru, dan berfungsi persis sama seperti jika Anda hanya ditugaskan menggunakan tanda sama dengan (=).

Ambil contoh ini.

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 suatu 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'

OK, sekarang mari kita berikan beberapa contoh menggunakan objek saja ... 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

OK, jika Anda membaca seluruh posting ini, mungkin Anda sekarang memiliki pemahaman yang lebih baik tentang cara kerja pemanggilan fungsi dalam JavaScript. Tidak masalah apakah sesuatu dilewatkan dengan referensi atau berdasarkan nilai ... Yang penting adalah penugasan vs mutasi.

Setiap kali Anda melewatkan variabel ke suatu fungsi, Anda "Menugaskan" ke apa pun nama variabel parameternya, sama seperti jika Anda menggunakan tanda sama dengan (=).

Selalu ingat bahwa tanda sama dengan (=) berarti tugas. Selalu ingat bahwa meneruskan parameter ke fungsi dalam JavaScript juga berarti tugas. Mereka sama dan 2 variabel terhubung dengan cara yang persis sama (artinya tidak, kecuali Anda menghitung bahwa mereka ditugaskan ke objek yang sama).

Satu-satunya waktu "memodifikasi variabel" mempengaruhi variabel yang berbeda adalah ketika objek yang mendasarinya dimutasi (dalam hal ini Anda belum mengubah variabel, tetapi objek itu sendiri.

Tidak ada gunanya membuat perbedaan antara objek dan primitif, karena ia bekerja dengan cara yang persis sama seolah-olah 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 pribadi untuk fungsi (karena itu)

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'

2
Untuk setiap programmer C, pikirkan char *. foo(char *a){a="hello";} tidak melakukan apa-apa, tetapi jika Anda melakukannya foo(char *a){a[0]='h';a[1]='i';a[2]=0;}diubah di luar karena alokasi memori dilewatkan oleh nilai yang mereferensikan string (char array). Melewati struct (mirip dengan objek js) dengan nilai dalam C diizinkan, tetapi tidak disarankan. JavaScript hanya menerapkan praktik terbaik ini dan menyembunyikan cruft yang tidak perlu dan biasanya tidak diinginkan ... dan tentu saja membuatnya lebih mudah dibaca.
technosaurus

2
Ini benar - istilah pass-by-value dan pass-by-reference memiliki makna dalam desain bahasa pemrograman, dan arti itu tidak ada hubungannya sama sekali dengan mutasi objek. Ini semua tentang cara kerja parameter fungsi.
Runcing

2
Sekarang saya mengerti bahwa obj1 = obj2 berarti bahwa obj1 dan obj2 sekarang menunjuk ke lokasi referensi yang sama, dan jika saya memodifikasi internal obj2, referensi obj1 akan mengekspos internal yang sama. Bagaimana cara menyalin objek sehingga ketika saya melakukan source = { "id":"1"}; copy = source /*this is wrong*/; copy.id="2"itu sumber masih {"id": "1"}?
Machtyn

1
Saya memposting jawaban lain dengan definisi tradisional untuk semoga mengurangi kebingungan. Definisi tradisional "pass-by-value" dan "pass-by-reference" didefinisikan kembali pada hari penunjuk memori sebelum dereferencing otomatis. Sangat dipahami dengan baik bahwa nilai variabel objek sebenarnya adalah lokasi penunjuk memori, bukan objek. Meskipun diskusi Anda tentang penugasan vs mutasi mungkin berguna, tidak perlu membuang istilah tradisional atau definisi mereka. Mutasi, penugasan, pass-by-value, pass-by-reference, dll. Tidak boleh saling bertentangan.
C Perkins

apakah "Angka" juga "tidak berubah"?
ebram khalil

72

Pertimbangkan yang berikut ini:

  1. Variabel adalah pointer ke nilai dalam memori.
  2. Menugaskan kembali sebuah variabel hanya menunjuk bahwa pointer pada nilai baru.
  3. Menugaskan kembali suatu variabel tidak akan pernah mempengaruhi variabel lain yang menunjuk pada objek yang sama

Jadi, lupakan "pass by reference / value" jangan terpaku pada "pass by reference / value" karena:

  1. Istilah tersebut hanya digunakan untuk menggambarkan perilaku suatu bahasa, belum tentu implementasi yang mendasari sebenarnya. Sebagai hasil dari abstraksi ini, rincian kritis yang penting untuk penjelasan yang layak hilang, yang pasti mengarah ke situasi saat ini di mana satu istilah tidak cukup menggambarkan perilaku aktual dan informasi tambahan harus disediakan
  2. Konsep-konsep ini awalnya tidak didefinisikan dengan maksud menggambarkan javascript khususnya dan jadi saya tidak merasa terdorong untuk 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

Pertanyaan tindak lanjut untuk kredit ekstra;) Bagaimana cara kerja pengumpulan sampah? Jika saya menggilir variabel melalui sejuta {'George', 1}nilai, tetapi hanya menggunakan salah satunya saja, lalu bagaimana yang lain dikelola? Dan apa yang terjadi ketika saya menetapkan variabel ke nilai variabel lain? Apakah saya kemudian menunjuk ke sebuah pointer, atau menunjuk ke pointee dari operan yang tepat? Apakah var myExistingVar = {"blah", 42}; var obj = myExistingVar;menghasilkan objmenunjuk ke {"blah", 42}, atau ke myExistingVar?
Michael Hoffmann

@MichaelHoffmann Ini layak pertanyaan SO mereka sendiri dan mungkin sudah dijawab lebih baik daripada yang bisa saya kelola. Yang sedang berkata, 1)saya menjalankan profil memori di alat dev browser untuk fungsi loop seperti yang Anda jelaskan dan melihat lonjakan dalam penggunaan memori selama proses looping. Ini tampaknya menunjukkan bahwa objek identik baru memang sedang dibuat di setiap iterasi dari loop. Ketika paku tiba-tiba jatuh, pengumpul sampah baru saja membersihkan sekelompok benda yang tidak digunakan ini.
geg

1
@MichaelHoffmann 2)Mengenai sesuatu seperti var a = b, javascript tidak menyediakan mekanisme untuk menggunakan pointer dan variabel tidak pernah dapat menunjuk ke pointer (seperti yang Anda dapat dalam C), meskipun mesin javascript yang mendasarinya pasti menggunakannya. Jadi ... var a = bakan mengarahkan a"ke titik operasi operan yang tepat"
geg

Saya mengajukan pertanyaan # 1 di sini (khususnya tentang Chrome karena penerapannya mungkin berbeda di setiap browser) stackoverflow.com/q/42778439/539997 dan saya masih mencoba memikirkan bagaimana cara menjawab pertanyaan kata # 2. Bantuan apa pun dihargai.
Michael Hoffmann

1
Tidak perlu melupakan "lewat referensi / nilai" ! Istilah-istilah ini memiliki makna historis yang menggambarkan dengan tepat apa yang Anda coba gambarkan. Jika kita membuang istilah dan definisi historis dan menjadi terlalu malas untuk mempelajari apa yang mereka maksudkan, maka kita kehilangan kemampuan untuk berkomunikasi secara efektif antar generasi. Tidak akan ada cara yang baik untuk membahas perbedaan antara berbagai bahasa dan sistem. Sebaliknya, programmer baru perlu belajar dan memahami istilah-istilah tradisional dan mengapa mereka berasal. Kalau tidak, kita secara kolektif kehilangan pengetahuan dan pemahaman.
C Perkins

24

Objek di luar fungsi dilewatkan ke fungsi dengan memberikan referensi ke objek luar.

Saat Anda menggunakan referensi itu untuk memanipulasi objeknya, objek di luar akan terpengaruh. Namun, jika di dalam fungsi Anda memutuskan untuk mengarahkan referensi ke sesuatu yang lain, Anda tidak mempengaruhi objek di luar sama sekali, karena semua yang Anda lakukan adalah mengarahkan kembali referensi ke sesuatu yang lain.


20

Pikirkan seperti ini: Itu selalu melewati nilai. Namun, nilai suatu objek bukanlah objek itu sendiri, tetapi referensi ke objek itu.

Berikut ini sebuah contoh, melewati 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

Mengulanginya dengan 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 lagi contoh:

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}

19

Sebuah penjelasan yang sangat rinci tentang menyalin, melewati dan membandingkan dengan nilai dan dengan referensi dalam bab ini dari "JavaScript: Panduan pasti" buku.

Sebelum kita meninggalkan topik memanipulasi objek dan array dengan referensi, kita perlu membersihkan titik nomenklatur.

Ungkapan "lulus dengan referensi" dapat memiliki beberapa arti. Untuk beberapa pembaca, frasa mengacu pada teknik doa fungsi yang memungkinkan fungsi untuk menetapkan nilai-nilai baru untuk argumennya dan agar nilai-nilai yang dimodifikasi tersebut terlihat di luar fungsi. Ini bukan cara istilah itu digunakan dalam buku ini.

Di sini, yang kami maksud hanyalah bahwa referensi ke suatu objek atau array - bukan objek itu sendiri - diteruskan ke suatu fungsi. Suatu fungsi dapat menggunakan referensi untuk memodifikasi properti dari objek atau elemen array. Tetapi jika fungsi menimpa referensi dengan referensi ke objek atau array baru, modifikasi itu tidak terlihat di luar fungsi.

Pembaca yang akrab dengan makna lain dari istilah ini mungkin lebih suka mengatakan bahwa objek dan array dilewatkan oleh nilai, tetapi nilai yang dilewatkan sebenarnya adalah referensi daripada objek itu sendiri.


Wow, ini sangat membingungkan. Siapa yang waras mereka akan mendefinisikan istilah mapan berarti sebaliknya dan kemudian menggunakannya seperti itu? Tidak heran begitu banyak jawaban di sini untuk pertanyaan ini sangat membingungkan.
Jörg W Mittag

16

JavaScript selalu dinilai berdasarkan nilai ; semuanya bertipe nilai.

Objek adalah nilai, dan fungsi anggota dari objek adalah nilai itu sendiri (ingat bahwa fungsi adalah objek kelas satu dalam JavaScript). Juga, berkenaan dengan konsep bahwa segala sesuatu dalam JavaScript adalah objek ; ini salah. String, simbol, angka, boolean, null, dan undefined adalah primitif .

Kadang-kadang mereka dapat memanfaatkan beberapa fungsi dan properti anggota yang diwarisi dari prototipe dasar mereka, tetapi ini hanya untuk kenyamanan. Itu 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 nilai yang tidak terdefinisi.


12
-1, tidak selalu lulus dengan nilai. Dari MDC: "Jika Anda melewatkan objek (yaitu nilai non-primitif, seperti Array atau objek yang ditentukan pengguna) sebagai parameter, referensi ke objek diteruskan ke fungsi."
Nick

37
@Nick: Itu selalu lewat nilai. Titik. Referensi ke objek diteruskan oleh nilai ke fungsi. Itu tidak lewat referensi. "Lulus dengan referensi" hampir bisa dianggap sebagai melewati variabel itu sendiri, bukan nilainya; setiap perubahan yang dilakukan fungsi pada argumen (termasuk menggantinya dengan objek yang berbeda sama sekali!) akan tercermin dalam pemanggil. Bit terakhir itu tidak mungkin di JS, karena JS tidak lulus dengan referensi - itu melewati referensi dengan nilai. Perbedaannya halus, tetapi lebih penting untuk memahami keterbatasannya.
cHao

1
Untuk penumpuk masa depan ... Tentang referensi Anda ini: x = "teste"; x.foo = 12;dll. Hanya karena properti tidak persisten, itu tidak berarti itu bukan objek. Seperti yang dikatakan MDN: Dalam JavaScript, hampir semuanya adalah objek. Semua tipe primitif kecuali null dan undefined diperlakukan sebagai objek. Mereka dapat diberi properti (properti yang ditugaskan dari beberapa jenis tidak persisten), dan mereka memiliki semua karakteristik objek. link
slacktracer

9
MDN adalah wiki yang diedit pengguna dan salah di sana. Referensi normatif adalah ECMA-262. Lihat S. 8 "Tipe Spesifikasi Referensi", yang menjelaskan bagaimana referensi diselesaikan, dan juga 8.12.5 "[[Put]]", yang digunakan untuk menjelaskan PenugasanExpression ke Referensi, dan, untuk objek coersion 9.9 ToObject. Untuk nilai primitif, Michael sudah menjelaskan apa yang ToObject lakukan, seperti dalam spesifikasi. Tapi lihat juga s. 4.3.2 nilai primitif.
Garrett

1
@WonderLand: Tidak, dia tidak. Orang-orang yang tidak pernah dapat melewati referensi mungkin tidak pernah memahami perbedaan antara melewati referensi dan melewati referensi berdasarkan nilai. Tapi mereka ada di sana, dan itu penting. Saya tidak peduli memberi informasi yang salah pada orang lain hanya karena itu terdengar lebih mudah.
cao

12

Dalam JavaScript, jenis nilai semata-mata mengontrol apakah nilai itu akan ditetapkan oleh copy-nilai atau oleh copy-referensi .

Nilai-nilai primitif selalu ditetapkan / diteruskan oleh copy-nilai :

  • null
  • undefined
  • tali
  • jumlah
  • boolean
  • simbol dalam ES6

Nilai gabungan selalu ditetapkan / diteruskan dengan salinan-rujukan

  • 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 2merupakan skalar primitif, amemegang satu salinan awal dari nilai itu, dan bdiberikan salinan lain dari nilai tersebut. Saat berubah b, Anda sama sekali tidak mengubah nilainya a.

Tetapi keduanya cdan dmerupakan referensi terpisah untuk nilai bersama yang sama [1,2,3], yang merupakan nilai majemuk. Penting untuk dicatat bahwa tidak ada catau dlebih "memiliki" [1,2,3]nilai - keduanya hanya referensi rekan yang setara dengan nilai. Jadi, ketika menggunakan salah satu referensi untuk memodifikasi ( .push(4)) nilai bersama aktual arrayitu sendiri, itu hanya mempengaruhi satu nilai yang dibagikan, dan kedua referensi akan referensi nilai yang baru dimodifikasi [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]

Ketika kami membuat tugas b = [4,5,6], kami sama sekali tidak melakukan apa pun untuk memengaruhi di mana amasih merujuk ( [1,2,3]). Untuk melakukan itu, bharus menjadi pointer adaripada referensi ke array- tetapi tidak ada kemampuan seperti itu ada 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 kami melewati argumen a, itu memberikan salinan areferensi ke x. xdan areferensi terpisah menunjuk pada nilai yang sama [1,2,3]. Sekarang, di dalam fungsi, kita bisa menggunakan referensi itu untuk mengubah nilai itu sendiri ( push(4)). Tetapi ketika kita membuat tugas x = [4,5,6], ini sama sekali tidak mempengaruhi di mana referensi awal amenunjuk - masih menunjuk pada nilai (sekarang dimodifikasi) [1,2,3,4].

Untuk secara efektif meneruskan nilai gabungan (seperti a array) dengan nilai-salinan, Anda perlu secara manual membuat salinannya, sehingga referensi yang diteruskan masih tidak menunjuk ke aslinya. Sebagai contoh:

foo( a.slice() );

Nilai majemuk (objek, array, dll) yang dapat dilewati oleh referensi-salinan

function foo(wrapper) {
    wrapper.a = 42;
}

var obj = {
    a: 2
};

foo( obj );

obj.a; // 42

Di sini, objbertindak sebagai pembungkus untuk properti primitif skalar a. Ketika diteruskan ke foo(..), salinan objreferensi dilewatkan dan diatur ke wrapperparameter. Kita sekarang dapat menggunakan wrapperreferensi untuk mengakses objek bersama, dan memperbarui propertinya. Setelah fungsi selesai, obj.aakan melihat nilai yang diperbarui 42.

Sumber


Anda pertama-tama menyatakan "Nilai gabungan selalu ditetapkan / diteruskan dengan salinan-rujukan", dan kemudian Anda menyatakan "memberikan salinan rujukan ke x". Dalam kasus apa yang Anda sebut "nilai gabungan", nilai variabel aktual ADALAH referensi (yaitu penunjuk memori). Seperti yang Anda jelaskan, referensi disalin ... sehingga nilai variabel disalin , sekali lagi menekankan bahwa REFERENSI ADALAH NILAI. Itu berarti JavaScript adalah nilai tambah untuk semua jenis. Pass-by-value berarti meneruskan salinan nilai variabel. Tidak masalah bahwa nilainya adalah referensi ke objek / array.
C Perkins

Anda memperkenalkan terminologi baru (copy-nilai / copy-referensi) dan itu hanya membuat segalanya menjadi lebih rumit. Hanya ada salinan, titik. Jika Anda melewatkan primitif Anda melewati salinan data primitif yang sebenarnya, jika Anda melewati suatu objek, Anda melewati salinan lokasi memori objek. Hanya itu yang perlu Anda katakan. Ada lagi yang lebih membingungkan orang.
Scott Marcus

9

baik, ini tentang 'kinerja' dan 'kecepatan' dan dalam kata sederhana 'manajemen memori' dalam bahasa pemrograman.

dalam javascript kita dapat meletakkan nilai dalam dua lapisan: type1 - objectsdan type2 -semua jenis nilai lainnya seperti string& boolean& dll

jika Anda membayangkan memori seperti kotak di bawah ini yang di masing-masingnya hanya satu nilai type2 yang dapat disimpan:

masukkan deskripsi gambar di sini

setiap type2-value (hijau) adalah kotak tunggal sementara nilai type1 (biru) adalah grup dari mereka :

masukkan deskripsi gambar di sini

intinya adalah bahwa jika Anda ingin menunjukkan nilai type2, alamatnya sederhana tetapi jika Anda ingin melakukan hal yang sama untuk nilai type1 itu tidak mudah sama sekali! :

masukkan deskripsi gambar di sini

dan dalam cerita yang lebih rumit:

masukkan deskripsi gambar di sini

jadi di sini referensi dapat menyelamatkan kita: masukkan deskripsi gambar di sini

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 jadi kami memindahkan panah hijau dengan nilai ke mana pun ia pergi dan dalam semua tugas, fungsi dan seterusnya ...

tetapi kita tidak dapat melakukan hal yang sama dengan panah ungu, kita mungkin ingin memindahkan sel 'john' di sini atau banyak hal lainnya ..., sehingga panah ungu akan menempel pada tempatnya dan hanya panah khas yang ditugaskan untuk itu akan bergerak ...

situasi yang sangat membingungkan adalah ketika Anda tidak dapat menyadari bagaimana perubahan variabel referensi Anda, 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'];

masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini


8

Ini sedikit penjelasan untuk pass by value dan pass by reference (JavaScript). Dalam konsep ini, mereka berbicara tentang melewati variabel dengan referensi dan melewati 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, angka, Boolean, undefined, dan null).
  • a dialokasikan memori (katakanlah 0x001) dan b membuat salinan nilai dalam memori (katakanlah 0x002).
  • Jadi mengubah nilai suatu variabel tidak memengaruhi yang lain, karena keduanya berada di dua lokasi yang berbeda.

Lewati dengan 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 menunjuk ke beberapa memori, misalnya (0x012).
  • Ketika d = c, dalam langkah ini dmenunjuk ke lokasi yang sama (0x012).
  • Mengubah nilai setiap perubahan nilai untuk kedua variabel.
  • Fungsi adalah objek

Kasus khusus, lulus dengan referensi (objek)

c = {"name" : "jane"};
console.log(c); // { "name" : "jane" }
console.log(d); // { "name" : "doe" }
  • Operator yang sama (=) mengatur ruang memori atau alamat baru

Dalam kasus khusus Anda, itu bukan operator penugasan yang menyebabkan alokasi ruang memori, itu adalah objek literal itu sendiri. Notasi braket curley menyebabkan penciptaan objek baru. Properti cdiatur ke salinan referensi objek baru.
georgeawg

6

berbagi apa yang saya ketahui tentang referensi dalam JavaScript

Dalam JavaScript, saat menetapkan objek ke variabel, nilai yang ditetapkan ke variabel adalah referensi ke objek:

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


1
Ini adalah jawaban yang terlalu sederhana yang tidak mengatakan apa pun yang jawaban sebelumnya tidak menjelaskan lebih baik. Saya bingung mengapa Anda memanggil array sebagai kasus khusus.
Quentin

1
" objek disimpan sebagai referensi " menyesatkan. Maksud saya maksud Anda adalah bahwa ketika menetapkan objek ke variabel, nilai yang diberikan ke variabel adalah referensi ke objek.
RobG

ini tidak mengatasi masalah memperbarui objek di dalam fungsi yang tidak memperbarui objek di luar fungsi. Itulah keseluruhan gambar yang tampaknya berfungsi sebagai nilai alih-alih referensi. Karenanya -1
amaster

@amaster Terima kasih telah menunjukkan itu! Bisakah Anda menyarankan suntingan?
xameeramir

Haha, saya mencoba ... edit yang saya sarankan terlalu banyak mengubah amd tidak diizinkan
amaster

4

Semantik!! Menetapkan definisi konkret akan membuat beberapa jawaban dan komentar tidak cocok karena mereka tidak menggambarkan hal yang sama bahkan ketika menggunakan kata dan frasa yang sama, tetapi sangat penting untuk melewati kebingungan (terutama untuk programmer baru).

Pertama-tama, ada beberapa tingkatan abstraksi yang tampaknya tidak semua orang pahami. Programer yang lebih baru yang telah belajar bahasa generasi ke-4 atau ke-5 mungkin mengalami kesulitan membungkus pikiran mereka di sekitar konsep yang akrab dengan perakitan atau programmer C tidak bertahap oleh pointer ke pointer ke pointer. Pass-by-reference tidak hanya berarti kemampuan untuk mengubah objek yang direferensikan menggunakan variabel parameter fungsi.

Variabel : Konsep gabungan simbol yang mereferensikan nilai pada lokasi tertentu dalam memori. Istilah ini biasanya terlalu dimuat untuk digunakan sendiri dalam membahas detail.

Simbol : String teks yang digunakan untuk merujuk ke variabel (yaitu nama variabel).

Nilai : Bit tertentu disimpan dalam memori dan direferensikan menggunakan simbol variabel.

Lokasi memori : Di mana nilai variabel disimpan. (Lokasi itu sendiri diwakili oleh angka yang terpisah dari nilai yang disimpan di lokasi.)

Parameter fungsi : Variabel dideklarasikan dalam definisi fungsi, digunakan untuk referensi variabel yang diteruskan ke fungsi.

Argumen fungsi : Variabel di luar fungsi yang diteruskan ke fungsi oleh pemanggil.

Variabel objek : Variabel yang nilai dasarnya yang mendasarinya bukan "objek" itu sendiri, melainkan nilainya adalah pointer (nilai lokasi memori) ke lokasi lain di memori tempat data aktual objek disimpan. Dalam sebagian besar bahasa generasi yang lebih tinggi, aspek "penunjuk" secara efektif disembunyikan oleh rujukan otomatis dalam berbagai konteks.

Variabel primitif : Variabel yang nilainya IS nilai sebenarnya. Bahkan konsep ini dapat menjadi rumit dengan konteks auto-boxing dan objek-seperti dari berbagai bahasa, tetapi ide-ide umum adalah bahwa nilai variabel adalah nilai aktual yang diwakili oleh simbol variabel daripada penunjuk ke lokasi memori lain.

Argumen fungsi dan parameter bukan hal yang sama. Juga, nilai variabel bukanlah objek variabel (seperti yang telah ditunjukkan oleh berbagai orang, tetapi tampaknya diabaikan). Perbedaan-perbedaan ini sangat penting untuk pemahaman yang tepat.

Pass-by-value atau Call-by-sharing (untuk objek): Nilai argumen fungsi COPIED ke lokasi memori lain yang direferensikan oleh simbol parameter fungsi (terlepas dari apakah itu di stack atau heap). Dengan kata lain, parameter fungsi menerima salinan dari nilai argumen yang diteruskan ... DAN (kritis) nilai argumen TIDAK PERNAH DIPERBARUI / DIUBAH / DIUBAH oleh fungsi pemanggilan. Ingat, nilai variabel objek BUKAN objek itu sendiri, melainkan itu adalah pointer ke objek, jadi meneruskan variabel objek dengan nilai menyalin pointer ke variabel parameter fungsi. Nilai parameter fungsi menunjuk ke objek yang sama persis di memori. Objek data itu sendiri dapat diubah secara langsung melalui parameter fungsi, TETAPI nilai argumen fungsi TIDAK PERNAH DIPERBARUI, sehingga akan terus menunjuk ke yang samaobjek di seluruh dan bahkan setelah pemanggilan fungsi (bahkan jika data objeknya diubah atau jika parameter fungsi diberikan objek yang berbeda sama sekali). Tidaklah tepat untuk menyimpulkan bahwa argumen fungsi disahkan oleh referensi hanya karena objek yang direferensikan dapat diperbarui melalui variabel parameter fungsi.

Referensi Panggilan / Lulus : Nilai argumen fungsi dapat / akan diperbarui secara langsung oleh parameter fungsi terkait. Jika itu membantu, parameter fungsi menjadi "alias" yang efektif untuk argumen - mereka secara efektif merujuk pada nilai yang sama di lokasi memori yang sama. Jika argumen fungsi adalah variabel objek, kemampuan untuk mengubah data objek tidak berbeda dari kasus pass-by-value karena parameter fungsi masih akan menunjuk ke objek yang sama dengan argumen. Tetapi dalam kasus variabel objek, jika parameter fungsi diatur ke objek yang sama sekali berbeda, maka argumen juga akan menunjuk ke objek yang berbeda - ini tidak terjadi dalam kasus pass-by-value.

JavaScript tidak lulus dengan referensi. Jika Anda membaca dengan seksama, Anda akan menyadari bahwa semua pendapat yang bertentangan salah mengerti apa yang dimaksud dengan nilai demi nilai dan mereka secara keliru menyimpulkan bahwa kemampuan untuk memperbarui data objek melalui parameter fungsi identik dengan "nilai demi nilai".

Objek klon / salin : Objek baru dibuat dan data objek asli disalin. Ini bisa berupa salinan dalam atau salinan dangkal, tetapi intinya adalah bahwa objek baru dibuat. Membuat salinan objek adalah konsep terpisah dari nilai per nilai. Beberapa bahasa membedakan antara objek kelas dan struct (atau sejenisnya), dan mungkin memiliki perilaku yang berbeda untuk meneruskan variabel dari tipe yang berbeda. Tetapi JavaScript tidak melakukan hal seperti ini secara otomatis ketika melewati variabel objek. Tetapi tidak adanya kloning objek otomatis tidak diterjemahkan menjadi pass-by-reference.


4

JavaScript melewati tipe primitif berdasarkan nilai dan tipe objek dengan referensi

Sekarang, orang suka bertengkar tanpa henti tentang apakah "lulus dengan referensi" adalah cara yang benar untuk menggambarkan apa yang Java et al. sebenarnya. Intinya adalah ini:

  1. Melewati objek tidak menyalin objek.
  2. Objek yang diteruskan ke suatu fungsi dapat membuat anggotanya dimodifikasi oleh fungsi tersebut.
  3. Nilai primitif yang diteruskan ke suatu fungsi tidak dapat dimodifikasi oleh fungsi. Salinan dibuat.

Dalam buku saya itu disebut lewat referensi.

- Brian Bi - Bahasa pemrograman manakah yang dilewati oleh referensi?


Memperbarui

Inilah bantahan untuk ini:

Tidak ada "pass by reference" yang tersedia di JavaScript.


@Amy Karena itu menggambarkan pass by value, bukan pass by reference. Jawaban ini adalah jawaban yang bagus yang menunjukkan perbedaan: stackoverflow.com/a/3638034/3307720
nasch

@nasch Saya mengerti perbedaannya. # 1 dan # 2 menggambarkan semantik pass-by-ref. # 3 menggambarkan semantik nilai-demi-nilai.
Amy

@Amy 1, 2, dan 3 semuanya konsisten dengan nilai by pass. Untuk mendapatkan referensi, Anda juga perlu 4: menetapkan referensi ke nilai baru di dalam fungsi (dengan operator =) juga menugaskan kembali referensi di luar fungsi. Ini tidak terjadi dengan Javascript, membuatnya secara eksklusif melewati nilai. Saat melewati objek, Anda meneruskan sebuah pointer ke objek, dan Anda melewati pointer itu dengan nilai.
nasch

Itu tidak umum apa yang dimaksud dengan "pass-by-reference". Anda telah memenuhi permintaan saya, dan saya tidak setuju dengan Anda. Terima kasih.
Amy

"Dalam buku saya itu disebut lewat referensi." - Di setiap buku kompiler, buku interpreter, buku teori bahasa pemrograman, dan buku ilmu komputer yang pernah ditulis, tidak.
Jörg W Mittag

3

Cara sederhana saya untuk memahami ini ...

  • Saat memanggil fungsi, Anda mengirimkan konten (referensi atau nilai) dari variabel argumen, bukan variabel itu sendiri.

    var var1 = 13;
    var var2 = { prop: 2 };
    
    //13 and var2's content (reference) are being passed here
    foo(var1, var2); 
  • Di dalam fungsi, variabel parameter, inVar1dan inVar2, menerima konten yang diteruskan.

    function foo(inVar1, inVar2){
        //changing contents of inVar1 and inVar2 won't affect variables outside
        inVar1 = 20;
        inVar2 = { prop: 7 };
    }
  • Sejak inVar2menerima referensi { prop: 2 }, Anda dapat mengubah nilai properti objek.

    function foo(inVar1, inVar2){
        inVar2.prop = 7; 
    }

3

Melewati argumen ke fungsi dalam JavaScript analog dengan melewati parameter dengan nilai pointer di 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;
}

1
Saya tidak berpikir ini adalah kasus dalam JavaScript: `` `javascript var num = 5;
Danail Nachev

@DanailNachev: Walaupun itu mungkin benar secara teknis, perbedaannya hanya dapat diamati untuk objek yang bisa berubah yang tidak dimiliki primitif ECMAScript.
Jörg W Mittag

3

Untuk pengacara bahasa pemrograman, saya telah membaca bagian ECMAScript 5.1 berikut ini (yang lebih mudah dibaca daripada edisi terakhir), dan menanyakannya di milis ECMAScript.

TL; DR : Everythings're dilewati oleh nilai, tetapi properti dari Objects adalah referensi, dan definisi Object sangat kurang dalam standar.

Konstruksi Daftar Argumen

Bagian 11.2.4 "Daftar Argumen" mengatakan yang berikut tentang membuat daftar argumen yang hanya terdiri dari 1 argumen:

ArgumentList produksi: AssignmentExpression dievaluasi sebagai berikut:

  1. Biarkan ref menjadi hasil evaluasi AssignmentExpression.
  2. Biarkan arg menjadi GetValue (ref).
  3. Kembalikan Daftar yang satu-satunya itemnya adalah arg.

Bagian ini juga menyebutkan kasus di mana daftar argumen memiliki 0 atau> 1 argumen.

Dengan demikian, semuanya dilewatkan dengan referensi.

Akses Properti Obyek

Bagian 11.2.1 "Aksesor Properti"

Produksi MemberExpression: MemberExpression [Ekspresi] dievaluasi sebagai berikut:

  1. Biarkan baseReference menjadi hasil mengevaluasi MemberExpression.
  2. Biarkan baseValue menjadi GetValue (baseReference).
  3. Biarkan propertyNameReference menjadi hasil evaluasi Ekspresi.
  4. Biarkan propertyNameValue menjadi GetValue (propertyNameReference).
  5. Sebut CheckObjectCoercible (baseValue).
  6. Biarkan propertyNameString menjadi ToString (propertyNameValue).
  7. Jika produksi sintaksis yang sedang dievaluasi terkandung dalam kode mode ketat, biarkan ketat menjadi benar, jika tidak maka ketat menjadi salah.
  8. Mengembalikan nilai Referensi tipe yang nilai dasarnya adalah BaseValue dan yang namanya direferensikan adalah propertyNameString, dan yang mode bendera ketatnya ketat.

Dengan demikian, properti Objek selalu tersedia sebagai referensi.

Tentang Referensi

Dijelaskan di bagian 8.7 "Tipe Spesifikasi Referensi", bahwa referensi tersebut bukan tipe nyata dalam bahasa - mereka hanya digunakan untuk menggambarkan perilaku dari delete, typeof, dan operator penugasan.

Definisi "Objek"

Didefinisikan dalam edisi 5.1 bahwa "Objek adalah kumpulan properti". Oleh karena itu, kita dapat menyimpulkan, bahwa nilai objek adalah koleksi, tetapi untuk apa nilai koleksi tidak didefinisikan dengan jelas dalam spesifikasi, dan memerlukan sedikit usaha untuk memahami.


Tidak pernah berhenti membuat saya takjub betapa banyak orang menjadi bingung oleh perbedaan antara argumen yang melewati nilai, argumen yang dilewati oleh referensi, operasi pada seluruh objek, dan operasi pada properti mereka. Pada tahun 1979, saya tidak mendapatkan gelar sarjana di bidang ilmu komputer, sebagai gantinya memilih untuk menambah 15 jam atau lebih pilihan CS ke program MBA saya. Namun demikian, segera menjadi jelas bagi saya bahwa pemahaman saya tentang konsep-konsep ini setidaknya sama baiknya dengan yang dipegang oleh kolega saya yang memiliki gelar dalam ilmu komputer atau matematika. Pelajari Assembler, dan itu akan menjadi sangat jelas.
David A. Gray

3

Dokumen MDN menjelaskannya dengan jelas, tanpa terlalu bertele-tele:

Parameter panggilan fungsi adalah argumen fungsi . Argumen diteruskan ke fungsi berdasarkan nilai . Jika fungsi mengubah nilai argumen, perubahan ini tidak tercermin secara global atau dalam fungsi panggilan. Namun, referensi objek adalah nilai juga, dan mereka khusus: jika fungsi mengubah properti objek yang dirujuk, perubahan itu terlihat di luar fungsi, (...)

Sumber: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions#Description


1

Dalam bahasa tingkat rendah, jika Anda ingin melewatkan variabel dengan referensi, Anda harus menggunakan sintaks tertentu dalam pembuatan fungsi:

int myAge = 14;
increaseAgeByRef(myAge);
function increaseAgeByRef(int &age) {
  *age = *age + 1;
}

The &ageadalah referensi ke myAge, tetapi jika Anda ingin nilai Anda harus mengkonversi referensi, menggunakan *age.

Javascript adalah bahasa tingkat tinggi yang melakukan konversi ini untuk Anda. Jadi, meskipun objek dilewatkan oleh referensi, bahasa mengubah parameter referensi ke nilai. Anda tidak perlu menggunakan &, pada definisi fungsi, untuk meneruskannya dengan referensi, juga *, pada badan fungsi, untuk mengkonversi referensi ke nilai, JS melakukannya untuk Anda.

Itu sebabnya ketika Anda mencoba mengubah objek di dalam suatu fungsi, dengan mengganti nilainya (yaitu age = {value:5}), perubahan itu tidak berlanjut, tetapi jika Anda mengubah sifatnya (yaitu age.value = 5), itu akan terjadi.

Belajarlah lagi


1

Saya sudah membaca jawaban ini beberapa kali, tetapi tidak BENAR-BENAR mendapatkannya sampai saya belajar tentang definisi teknis "Panggilan dengan berbagi" seperti yang disebut oleh Barbara Liskov

Semantik panggilan dengan berbagi berbeda dari panggilan dengan referensi dalam tugas untuk argumen fungsi dalam fungsi tidak terlihat oleh pemanggil (tidak seperti oleh referensi semantik) [rujukan?], Jadi misalnya jika suatu variabel dilewatkan, itu tidak mungkin untuk mensimulasikan tugas pada variabel itu dalam ruang lingkup pemanggil. Namun, karena fungsi memiliki akses ke objek yang sama dengan penelepon (tidak ada salinan yang dibuat), mutasi ke objek-objek tersebut, jika objek dapat berubah, dalam fungsi terlihat oleh pemanggil, yang mungkin tampak berbeda dari panggilan berdasarkan nilai. semantik. Mutasi objek yang bisa berubah dalam fungsi dapat dilihat oleh pemanggil karena objek tidak disalin atau dikloning - dibagikan.

Artinya, referensi parameter dapat diubah jika Anda pergi dan mengakses nilai parameter itu sendiri. Di sisi lain, penugasan ke parameter akan hilang setelah evaluasi, dan tidak dapat diakses oleh pemanggil fungsi.


Tidak, apakah suatu objek bisa berubah atau tidak bukan masalah sebenarnya. Semuanya selalu dilewati oleh nilai. Itu hanya tergantung pada apa yang Anda lewati (nilai atau referensi). Lihat ini .
Scott Marcus

Apa yang dia gambarkan adalah melewati referensi BY-VALUE. Tidak ada alasan untuk memperkenalkan terminologi baru.
Sanjeev

1

Cara yang paling sederhana

// Copy JS object without reference
var first = {"a":"value1","b":"value2"};
var clone = JSON.parse( JSON.stringify( first ) ); 

var second = ["a","b","c"];
var clone = JSON.parse( JSON.stringify( second ) ); 

JSON.parse( JSON.stringify( obj ) )adalah cara yang mengerikan untuk mengkloning objek. Tidak hanya itu tidak hanya lambat, tetapi juga dapat menyebabkan kehilangan data.
D. Pardal

0

Saya telah menemukan metode perluasan dari perpustakaan Underscore.js sangat berguna ketika saya ingin mengirimkan objek sebagai parameter yang dapat dimodifikasi atau diganti seluruhnya.

function replaceOrModify(aObj) {
  if (modify) {

    aObj.setNewValue('foo');

  } else {

   var newObj = new MyObject();
   // _.extend(destination, *sources) 
   _.extend(newObj, aObj);
  }
}

0

Penjelasan paling ringkas yang saya temukan ada di panduan gaya AirBNB :

  • Primitif : Ketika Anda mengakses tipe primitif, Anda bekerja langsung pada nilainya

    • tali
    • jumlah
    • boolean
    • batal
    • tidak terdefinisi

Misalnya:

var foo = 1,
    bar = foo;

bar = 9;

console.log(foo, bar); // => 1, 9
  • Kompleks : Ketika Anda mengakses tipe kompleks Anda bekerja pada referensi nilainya

    • obyek
    • Himpunan
    • fungsi

Misalnya:

var foo = [1, 2],
    bar = foo;

bar[0] = 9;

console.log(foo[0], bar[0]); // => 9, 9

Yaitu tipe primitif secara efektif dilewatkan oleh nilai, dan tipe kompleks dilewatkan oleh referensi.


Tidak, semuanya selalu dilewati oleh nilai. Itu hanya tergantung pada apa yang Anda lewati (nilai atau referensi). Lihat ini .
Scott Marcus

-1

Saya akan mengatakan itu pass-by-copy -

Pertimbangkan argumen dan objek variabel adalah objek yang dibuat selama konteks eksekusi yang dibuat di awal pemanggilan fungsi - dan nilai / referensi Anda yang sebenarnya diteruskan ke fungsi, simpan saja dalam argumen ini + objek variabel.

Secara sederhana, untuk tipe primitif, nilai-nilai disalin di awal pemanggilan fungsi, untuk tipe objek, referensi disalin.


1
"pass-by-copy" === nilai lewat
Scott Marcus

-1

Ada beberapa diskusi tentang penggunaan istilah "pass by reference" di JavaScript di sini , tetapi untuk menjawab pertanyaan Anda:

Suatu objek secara otomatis diteruskan dengan referensi, tanpa perlu secara khusus menyatakannya

(Dari artikel yang disebutkan di atas.)


7
Artikel yang ditautkan tidak lagi menyertakan pernyataan dan menghindari penggunaan "pass by reference" sama sekali.
C Perkins

Nilainya adalah referensi

-2

Cara mudah untuk menentukan apakah sesuatu itu "lewat referensi" adalah apakah Anda bisa menulis fungsi "tukar". Misalnya, dalam C, Anda dapat melakukan:

void swap(int *i, int *j)
{
    int t;
    t = *i;
    *i = *j;
    *j = t;
}

Jika Anda tidak dapat melakukan yang setara dengan itu di JavaScript, itu bukan "lewat referensi".


21
Ini tidak benar-benar lulus dengan referensi. Anda melewatkan pointer ke dalam fungsi, dan pointer itu diteruskan oleh nilai. Contoh yang lebih baik adalah kata kunci C ++ & operator atau kata kunci "ref" C #, keduanya benar-benar lulus dengan referensi.
Matt Greer

Yang lebih mudah adalah bahwa semuanya dilewatkan oleh nilai dalam JavaScript.
Scott Marcus


-3
  1. variabel tipe primitif seperti string, angka selalu dianggap sebagai nilai.
  2. Array dan Object dilewatkan sebagai pass by reference atau pass by value berdasarkan kedua kondisi ini.

    • jika Anda mengubah nilai Objek atau array dengan Objek atau Array baru, maka nilai tersebut diteruskan oleh Value.

      object1 = {item: "car"}; array1=[1,2,3];

    di sini Anda menugaskan objek atau array baru ke yang lama. Anda tidak mengubah nilai properti dari objek lama.

    • jika Anda mengubah nilai properti dari suatu objek atau array maka itu dilewatkan oleh Referensi.

      object1.key1= "car"; array1[0]=9;

    di sini Anda mengubah nilai properti objek lama. Anda tidak menetapkan objek atau array baru ke yang lama.

Kode

    function passVar(object1, object2, number1) {

        object1.key1= "laptop";
        object2 = {
            key2: "computer"
        };
        number1 = number1 + 1;
    }

    var object1 = {
        key1: "car"
    };
    var object2 = {
        key2: "bike"
    };
    var number1 = 10;

    passVar(object1, object2, number1);
    console.log(object1.key1);
    console.log(object2.key2);
    console.log(number1);

Output: -
    laptop
    bike
    10

1
Operator penugasan tidak perlu bingung dengan panggilan fungsi. Saat Anda menetapkan data baru ke variabel yang sudah ada, jumlah referensi dari data lama berkurang dan data baru dikaitkan dengan variabel lama. Pada dasarnya, variabel akhirnya menunjuk ke data baru. Hal yang sama berlaku untuk variabel properti. Karena penugasan ini bukan panggilan fungsi, tidak ada hubungannya dengan pass-by-value atau pass-by-reference.
John Sonderson

1
Tidak, semuanya selalu dilewati oleh nilai. Itu hanya tergantung pada apa yang Anda lewati (nilai atau referensi). Lihat ini .
Scott Marcus

-3
  1. Primitif (angka, Boolean, dll.) Dilewati oleh nilai.
    • String tidak berubah, jadi itu tidak masalah bagi mereka.
  2. Objek dilewatkan dengan referensi (referensi dilewatkan oleh nilai).

Tidak, semuanya selalu dilewati oleh nilai. Itu hanya tergantung pada apa yang Anda lewati (nilai atau referensi). Lihat ini .
Scott Marcus

Pernyataan kedua Anda bertentangan dengan dirinya sendiri.
Jörg W Mittag

-5

Nilai-nilai sederhana di dalam fungsi tidak akan mengubah nilai-nilai di luar fungsi (mereka dilewatkan oleh nilai), sedangkan yang kompleks akan (mereka dilewatkan oleh referensi).

function willNotChange(x) {

    x = 1;
}

var x = 1000;

willNotChange(x);

document.write('After function call, x = ' + x + '<br>'); // Still 1000

function willChange(y) {

    y.num = 2;
}

var y = {num: 2000};

willChange(y);
document.write('After function call y.num = ' + y.num + '<br>'); // Now 2, not 2000

itu konyol, y akan berubah karena pelingkupan tingkat fungsional, itu diangkat bukan karena dilewatkan oleh referensi.
Parijat Kalia

Tidak, semuanya selalu dilewati oleh nilai. Itu hanya tergantung pada apa yang Anda lewati (nilai atau referensi). Lihat ini .
Scott Marcus
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.