Apa itu value dalam javascript?

Tipe primitif (Angka, String, dll.) Dilewatkan oleh nilai, tetapi Objek tidak diketahui, karena keduanya dapat lewat-oleh-nilai (jika kami menganggap bahwa variabel yang memegang objek sebenarnya 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?

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 itu murni lewat nilai, maka mengubah obj1.item tidak akan berpengaruh pada obj1 di luar fungsi.
  • Jika itu murni lewat referensi, maka semuanya akan berubah. num akan menjadi 100, dan obj2.item akan membaca "changed".

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

Dalam istilah praktis, ini berarti bahwa jika Anda mengubah parameter itu sendiri (seperti dengan num dan obj2), itu tidak akan mempengaruhi item yang dimasukkan ke dalam parameter. Tetapi jika Anda mengubahINTERNALdari parameter, itu akan menyebar kembali (seperti dengan obj1).

Itu selalu melewati nilai, tetapi untuk objek nilai variabel adalah referensi. Karena itu, ketika Anda melewatkan objek dan mengubah anggota , perubahan itu tetap ada di luar fungsi. Ini membuatnya terlihat seperti lewat referensi. 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

Variabel tidak "memegang" objek, itu memegang referensi. Anda dapat menetapkan referensi itu ke variabel lain, sekarang keduanya mereferensikan 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 JS didukung lewat referensi.

2 Cents 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 dengan 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 cukup melakukan sedikit tugas 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 string tidak berubah dan kami tidak mengubahnya. Yang kami lakukan adalah "RE ASSIGN" favoriteString ke string baru. Ini pada dasarnya memutusnya dari string1. Dalam contoh sebelumnya, ketika kami mengganti nama objek kami, kami tidak menetapkan apa pun. (Yah, sebenarnya ... kami lakukan, kami menetapkan properti nama ke string baru.) Sebaliknya, kami hanya bermutasi objek yang membuat koneksi antara 2 variabel dan objek yang mendasarinya.

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 sama dengan (=) tanda.

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
otherObj = {
    firstName: 'Jack',
    lastName: 'Frost'
};

// Now, otherObj and myObject are assigned to 2 very different objects
// And mutating one object no longer mutates 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 no longer mutates 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 bagaimana panggilan fungsi bekerja di javascript. Tidak masalah apakah sesuatu dilewatkan dengan referensi atau dengan 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 suatu fungsi juga berarti tugas. Mereka sama dan 2 variabel terhubung dengan cara yang persis sama.

Satu-satunya waktu memodifikasi variabel mempengaruhi variabel yang berbeda adalah ketika objek yang mendasarinya dimutasi.

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'

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

Begitu, lupakan "lewat referensi/nilai" jangan terpaku pada "pass by reference/value" karena:

  1. Istilah ini hanya digunakan untuk menggambarkan perilaku dari suatu bahasa, belum tentu implementasi mendasar yang 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

Objek di luar fungsi dilewatkan ke fungsi dengan memberikan referensi ke luar obejct. 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.

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}

Javascript selalu pass-by-value , semuanya bertipe nilai. Objek adalah nilai, fungsi anggota dari objek adalah nilai itu sendiri (ingat bahwa fungsi adalah objek kelas satu dalam Javascript). Juga, mengenai konsep bahwa semua yang ada di Javascript adalah objek , ini salah. String, simbol, angka, boolean, null, dan undefined adalah primitif . Kadang-kadang mereka dapat memanfaatkan beberapa fungsi anggota dan properti yang diwarisi dari prototipe dasar mereka, tetapi ini hanya untuk kenyamanan, tidak berarti bahwa mereka adalah objek itu sendiri. Coba yang berikut untuk referensi

x = "test";
alert(x.foo);
x.foo = 12;
alert(x.foo);

Di kedua peringatan Anda akan menemukan nilai yang tidak terdefinisi.

Dalam JavaScript, jenis nilai hanya mengontrol apakah nilai itu akan ditetapkan oleh value-copy atau oleh reference-copy .

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

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

Nilai gabungan selalu diberikan/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 2 adalah skalar primitive, a memiliki satu salinan awal dari nilai itu, dan b diberikan salinan lain dari nilai tersebut. Saat mengubah b, Anda sama sekali tidak mengubah nilai dalam a.

Tetapi c dan d keduanya merupakan referensi terpisah untuk nilai bersama yang sama [1,2,3], yang merupakan nilai majemuk. Penting untuk dicatat bahwa c atau d tidak lebih "memiliki" nilai [1,2,3] - keduanya hanya referensi rekan yang setara dengan nilai tersebut. Jadi, ketika menggunakan salah satu referensi untuk memodifikasi (.Push(4)) nilai array yang dibagikan itu sendiri, itu mempengaruhi hanya satu nilai yang dibagikan, dan kedua referensi akan mereferensikan 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 a masih merujuk ([1,2,3]). Untuk melakukan itu, b harus menjadi penunjuk ke a daripada 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 kita meneruskan argumen a, itu memberikan salinan referensi a ke x. x dan a adalah referensi terpisah yang menunjuk pada nilai [1,2,3] yang sama. 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 a menunjuk - masih menunjuk pada nilai [1,2,3,4] (sekarang dimodifikasi).

Untuk secara efektif meneruskan nilai majemuk (seperti array) dengan nilai-salin, 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 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. Ketika diteruskan ke foo(..), salinan referensi obj dilewatkan dan disetel ke wrapperparameter. Kita sekarang dapat menggunakan referensi wrapper untuk mengakses objek yang dibagikan, dan memperbarui propertinya. Setelah fungsi selesai, obj.a akan melihat nilai yang diperbarui 42.

Sumber

Penjelasan yang sangat terperinci tentang menyalin, meneruskan, dan membandingkan berdasarkan nilai dan referensi ada di bab ini dari buku "JavaScript: The Definitive Guide".

Sebelum kita meninggalkan topik memanipulasi objek dan array dengan referensi, kita perlu menjernihkan 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 tersebut digunakan dalam buku ini. Di sini, yang kami maksud hanyalah bahwa referensi ke objek atau array - bukan objek itu sendiri - diteruskan ke suatu fungsi. Fungsi dapat menggunakan referensi untuk memodifikasi properti 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.

Satu hal yang masih belum saya pahami. Periksa kode di bawah ini. Adakah pikiran?

function A() {}
A.prototype.foo = function() {
    return 'initial value';
}


function B() {}
B.prototype.bar = A.prototype.foo;

console.log(A.prototype.foo()); //initial value
console.log(B.prototype.bar()); //initial value

A.prototype.foo = function() {
    return 'changed now';
}

console.log(A.prototype.foo()); //changed now
console.log(B.prototype.bar()); //Why still 'initial value'???

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 meminta 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. Sebuah

Semantik!! Menetapkan definisi konkret tentu akan membuat beberapa jawaban dan komentar tidak kompatibel 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 bagi programmer atau programmer C tidak secara bertahap oleh pointer ke pointer ke pointer. Pass-by-reference tidak hanya berarti kemampuan untuk mengubah objek yang direferensikan menggunakan variabel parameter fungsi.

Variable : Konsep gabungan dari 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 (mis. 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 penunjuk (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 umumnya adalah bahwa nilai variabel IS nilai aktual diwakili oleh simbol variabel daripada pointer ke memori lain lokasi.

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 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 IS TIDAK PERNAH DIPERBARUI/DIUBAH/DIUBAH oleh fungsi panggilan. 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. Data objek itu sendiri dapat diubah secara langsung melalui parameter fungsi, TETAPI nilai argumen fungsi IS TIDAK PERNAH DIPERBARUI, sehingga akan terus menunjuk ke objek sama di seluruh dan bahkan setelah fungsi panggilan (bahkan jika data objeknya telah diubah atau jika parameter fungsi ditetapkan 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.

Call/Pass-by-reference : Nilai argumen fungsi dapat/akan diperbarui secara langsung oleh parameter fungsi yang sesuai. Jika itu membantu, parameter fungsi menjadi "alias" yang efektif untuk argumen - mereka secara efektif merujuk ke 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 clone/copy : Objek baru dibuat dan data objek asli disalin. Ini bisa berupa salinan yang dalam atau salinan yang 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 jenis 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.

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

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

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

(Dari artikel yang disebutkan di atas.)

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 JS (string, number, boolean, undefined, 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.

Lulus 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 JS menetapkan objek ke variabel c, itu menunjuk ke beberapa memori katakanlah (0x012)
  • ketika d = c, pada langkah ini d menunjuk ke lokasi yang sama (0x012).
  • mengubah nilai setiap nilai perubahan 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

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

Semantik panggilan dengan berbagi berbeda dari panggilan dengan referensi dalam bahwa penugasan ke argumen fungsi di dalam fungsi tidak terlihat oleh pemanggil (tidak seperti oleh semantik referensi) [rujukan?], Jadi mis. jika suatu variabel disahkan, 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 dibuat), mutasi ke objek-objek tersebut, jika objek dapat berubah, dalam fungsi terlihat oleh penelepon, yang mungkin tampak berbeda dari panggilan dengan nilai semantik. Mutasi objek yang bisa berubah dalam fungsi dapat dilihat oleh penelepon 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.

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, inVar1 dan 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 };
    }
    
  • Karena inVar2 menerima referensi dari { prop: 2 }, Anda dapat mengubah nilai properti objek.

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

Untuk pengacara bahasa pemrograman, saya telah melalui bagian ECMAScript 5.1 berikut (yang lebih mudah dibaca daripada edisi terbaru), dan sejauh meminta di milis ECMAScript.

TL; DR: Segala sesuatu disahkan oleh nilai, tetapi properti Object adalah referensi, dan definisi Object secara menyeramkan 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-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. Kembalikan 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 penghapusan, 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 membutuhkan sedikit upaya untuk memahami.

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

&age adalah 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 melewatinya dengan referensi, baik *, pada fungsi tubuh, untuk mengkonversi referensi ke nilai, JS melakukannya untuk Anda.

Itulah sebabnya ketika Anda mencoba mengubah objek di dalam suatu fungsi, dengan mengganti nilainya (mis. age = {value:5}), perubahan itu tidak berlanjut, tetapi jika Anda mengubah propertinya (mis. age.value = 5), ya.

Belajarlah lagi

berbagi apa yang saya ketahui tentang referensi dalam javascript

Dalam Javascript, objects disimpan sebagai referensi:

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

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

  • Primitives : Ketika Anda mengakses tipe primitif Anda bekerja langsung pada nilainya

    • tali
    • jumlah
    • boolean
    • batal
    • undefined

Misalnya.:

var foo = 1,
    bar = foo;

bar = 9;

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

    • obyek
    • array
    • fungsi

Misalnya.:

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

bar[0] = 9;

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

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

Cara mudah untuk menentukan apakah sesuatu itu "lewat referensi" adalah apakah Anda dapat 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 hal yang sama dalam Javascript, itu bukan "lewat referensi".

Saya telah menemukan metode extended dari Underscore.js library 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);
  }
}

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

Parameter panggilan fungsi adalah fungsi argumen . Argumen diteruskan ke fungsi oleh 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/Fungsi#Description

  1. Primitif (Angka, Boolean) diberikan nilai.
    • String tidak berubah, jadi itu tidak masalah bagi mereka.
  2. Objek dilewatkan dengan referensi (referensi dilewatkan oleh nilai)

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 aktual Anda yang diteruskan ke fungsi hanya tersimpan dalam argumen ini + objek variabel.

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