memperkenalkan
Dalam bab ini, kami akan menjelaskan strategi parameter lulus untuk fungsi fungsi dalam ecmascript.
Dalam ilmu komputer, strategi ini umumnya disebut "strategi evaluasi" (Catatan Paman: Beberapa orang mengatakan itu diterjemahkan ke dalam strategi evaluasi, sementara yang lain menerjemahkannya ke dalam strategi penugasan. Melihat konten berikut, saya pikir lebih tepat untuk menyebutnya strategi penugasan. Bagaimanapun, judulnya harus ditulis sebagai strategi evaluasi yang mudah dipahami semua orang). Misalnya, dalam bahasa pemrograman, menetapkan aturan untuk evaluasi atau ekspresi perhitungan. Strategi untuk menyampaikan parameter ke suatu fungsi adalah kasus khusus.
http://dmitrysoshnikov.com/ecmascript/chapter-8-evaluuation-strategy/
Alasan untuk menulis artikel ini adalah bahwa seseorang di forum meminta untuk secara akurat menjelaskan beberapa strategi untuk parameter yang lewat. Kami telah memberikan definisi yang sesuai di sini, berharap ini akan membantu semua orang.
Banyak programmer yakin bahwa dalam JavaScript (bahkan dalam beberapa bahasa lain), objek dilewatkan dengan referensi, sedangkan tipe nilai asli dilewatkan oleh nilai. Selain itu, banyak artikel berbicara tentang "fakta" ini, tetapi banyak orang benar -benar memahami istilah ini, dan berapa banyak yang benar? Kami akan menjelaskannya satu per satu di artikel ini.
Teori Umum
Perlu dicatat bahwa dalam teori penugasan, umumnya ada dua strategi penugasan: ketat - berarti bahwa parameter dihitung sebelum memasuki program; Non -Strict - berarti bahwa perhitungan parameter dihitung berdasarkan persyaratan perhitungan (yaitu, setara dengan penundaan perhitungan).
Kemudian, di sini kita mempertimbangkan strategi transfer parameter fungsi dasar, yang sangat penting dari titik awal ecmascript. Hal pertama yang perlu diperhatikan adalah bahwa strategi lewat parameter yang ketat digunakan dalam ecmascript (bahkan dalam bahasa lain seperti C, Java, Python dan Ruby).
Selain itu, urutan perhitungan parameter yang ditularkan juga sangat penting - dalam ecmascript, itu dari kiri ke kanan, dan urutan pengantar (tidak dari kanan ke kanan) yang diimplementasikan dalam bahasa lain juga dapat digunakan.
Strategi transmisi yang ketat juga dibagi menjadi beberapa strategi benih, dan kami akan membahas strategi paling penting secara rinci dalam bab ini.
Tidak semua strategi yang dibahas di bawah ini digunakan dalam ecmascript, jadi ketika membahas perilaku spesifik dari strategi ini, kami menggunakan kode semu untuk menunjukkannya.
Melewati nilai
Melewati nilai, banyak pengembang sangat sadar bahwa nilai parameter adalah salinan nilai objek yang dilewati oleh penelepon. Mengubah nilai parameter di dalam fungsi tidak akan mempengaruhi objek luar (nilai parameter di luar). Secara umum, memori baru dialokasikan kembali (kami tidak memperhatikan bagaimana memori yang dialokasikan diimplementasikan - juga merupakan tumpukan atau alokasi memori dinamis). Nilai blok memori baru adalah salinan objek eksternal, dan nilainya digunakan di dalam fungsi.
Salinan kode adalah sebagai berikut:
bar = 10
Prosedur foo (bararg):
bararg = 20;
akhir
foo (bar)
// Ubah nilai di dalam foo tidak akan mempengaruhi nilai bilah internal
Cetak (bar) // 10
Namun, jika parameter fungsi bukan nilai asli tetapi objek struktural yang kompleks, itu akan membawa masalah kinerja yang hebat. C ++ memiliki masalah ini. Saat meneruskan struktur sebagai nilai ke dalam fungsi, itu adalah salinan lengkap.
Mari kita berikan contoh umum, gunakan strategi penugasan berikut untuk mengujinya. Pikirkan fungsi yang menerima 2 parameter. Parameter pertama adalah nilai objek, dan yang kedua adalah tanda boolean untuk menandai apakah objek sepenuhnya dimodifikasi (menugaskan kembali objek ke objek), atau hanya beberapa properti objek yang dimodifikasi.
Salinan kode adalah sebagai berikut:
// Catatan: Berikut ini adalah semua kode pseudo, bukan implementasi JS
bar = {
X: 10,
Y: 20
}
Prosedur FOO (bararg, isfullchange):
Jika isuffullchange:
bararg = {z: 1, q: 2}
KELUAR
akhir
Bararg.x = 100
Bararg.y = 200
akhir
foo (bar)
// Lewati Nilai, Objek Eksternal tidak akan diubah
print (bar) // {x: 10, y: 20}
// Ubah sepenuhnya objek (tetapkan nilai baru)
foo (bar, true)
// Tidak ada perubahan juga
print (bar) // {x: 10, y: 20}, bukan {z: 1, q: 2}
Lulus dengan referensi
PASS-by-referensi lain yang terkenal bukanlah salinan nilai, tetapi referensi implisit untuk objek, seperti alamat referensi langsung dari objek di luar. Setiap perubahan pada parameter di dalam fungsi mempengaruhi nilai objek di luar fungsi, karena keduanya merujuk objek yang sama, yaitu, parameter setara dengan alias objek eksternal.
Pseudocode:
Salinan kode adalah sebagai berikut:
Prosedur FOO (bararg, isfullchange):
Jika isuffullchange:
bararg = {z: 1, q: 2}
KELUAR
akhir
Bararg.x = 100
Bararg.y = 200
akhir
// Gunakan objek yang sama seperti di atas
bar = {
X: 10,
Y: 20
}
// Hasil panggilan dengan referensi adalah sebagai berikut:
foo (bar)
// Nilai properti objek telah diubah
print (bar) // {x: 100, y: 200}
// menugaskan kembali nilai -nilai baru juga mempengaruhi objek
foo (bar, true)
// Objek ini sudah menjadi objek baru
print (bar) // {z: 1, q: 2}
Strategi ini dapat dengan lebih efisien meneruskan objek yang kompleks, seperti objek struktur besar dengan batch besar atribut.
Hubungi dengan berbagi
Semua orang tahu dua strategi di atas, tetapi Anda mungkin tidak tahu strategi yang ingin Anda bicarakan di sini (sebenarnya, ini adalah strategi akademik). Namun, kita akan segera melihat bahwa ini adalah strategi yang memainkan peran kunci dalam strategi pengiriman parameter dalam ecmascript.
Strategi ini juga memiliki beberapa sinonim: "Lulus dengan Objek" atau "Lulus dengan berbagi objek".
Strategi ini diusulkan pada tahun 1974 oleh Barbara Liskov untuk bahasa pemrograman CLU.
Titik kunci dari strategi ini adalah bahwa fungsi menerima salinan (salinan) objek, dan salinan referensi dikaitkan dengan parameter formal dan nilainya.
Kami tidak dapat memanggil referensi yang muncul di sini "melewati referensi", karena parameter yang diterima oleh fungsi bukan alias objek langsung, tetapi salinan alamat referensi.
Perbedaan yang paling penting adalah bahwa fungsi menugaskan nilai -nilai baru ke parameter di dalam tidak akan mempengaruhi objek eksternal (seperti kasus yang dilewati dengan referensi dalam contoh di atas), tetapi karena parameter adalah salinan alamat, objek yang sama diakses di luar dan di dalam (misalnya, objek eksternal bukan salinan lengkap karena Anda ingin meneruskan nilai). Mengubah nilai atribut objek parameter akan mempengaruhi objek eksternal.
Salinan kode adalah sebagai berikut:
Prosedur FOO (bararg, isfullchange):
Jika isuffullchange:
bararg = {z: 1, q: 2}
KELUAR
akhir
Bararg.x = 100
Bararg.y = 200
akhir
// masih menggunakan struktur objek ini
bar = {
X: 10,
Y: 20
}
// melewati kontribusi akan mempengaruhi objek
foo (bar)
// Properti objek telah dimodifikasi
print (bar) // {x: 100, y: 200}
// penugasan kembali tidak berhasil
foo (bar, true)
// Masih nilai di atas
print (bar) // {x: 100, y: 200}
Asumsi pemrosesan ini adalah bahwa objek yang digunakan dalam sebagian besar bahasa bukan nilai asli.
Pass by Share adalah kasus khusus untuk melewati nilai
Strategi pengiriman dengan berbagi digunakan dalam banyak bahasa: java, ecmascript, python, ruby, visual basic, dll. Selain itu, komunitas Python telah menggunakan istilah ini, dan dapat digunakan dalam bahasa lain, karena nama lain cenderung membuat orang merasa bingung. Dalam kebanyakan kasus, seperti di Java, Ecmascript, atau Visual Basic, strategi ini juga disebut nilai pass -by -value - Makna: Nilai Khusus - Salinan Referensi (Salin).
Di satu sisi, seperti ini - parameter yang diteruskan ke fungsi hanyalah nama nilai terikat (alamat referensi) dan tidak akan mempengaruhi objek eksternal.
Di sisi lain, istilah -istilah ini benar -benar dianggap salah tanpa mempelajarinya, karena banyak forum berbicara tentang cara meneruskan objek ke fungsi javascript).
Memang ada pepatah dalam teori umum yang melewati nilai: tetapi pada saat ini nilai ini adalah apa yang kita sebut salinan alamat (salin), sehingga tidak melanggar aturan.
Di Ruby, strategi ini disebut Pass dengan referensi. Izinkan saya mengatakan lagi: tidak disahkan dalam hal salinan struktur besar (misalnya, bukan berdasarkan nilai), dan di sisi lain, kami tidak memproses referensi ke objek asli dan tidak dapat memodifikasinya; Oleh karena itu, konsep istilah lintas jangka waktu ini mungkin lebih membingungkan.
Tidak ada kasus khusus yang disahkan dengan referensi dalam teori seperti kasus khusus yang disahkan oleh nilai.
Tetapi masih perlu untuk memahami strategi -strategi ini dalam teknologi yang disebutkan di atas (Java, Ecmascript, Python, Ruby, lainnya). Bahkan, strategi yang mereka gunakan adalah lulus dengan berbagi.
Tekan Bagikan dan Pointer
Untuk с/с ++, strategi ini sama dengan melewati nilai pointer, tetapi ada perbedaan penting - strategi ini dapat dereferensi pointer dan sepenuhnya mengubah objek. Namun, secara umum, mengalokasikan penunjuk nilai (alamat) ke blok memori baru (yaitu, blok memori yang dirujuk sebelumnya tetap tidak berubah); Mengubah sifat objek melalui pointer akan mempengaruhi objek eksternal Adong.
Jadi, dan kategori pointer, kita dapat dengan jelas melihat bahwa ini dilewatkan oleh nilai alamat. Dalam hal ini, lewat dengan berbagi hanyalah "gula sintetis", seperti perilaku penugasan pointer (tetapi bukan dereferensi), atau memodifikasi sifat seperti referensi (tidak diperlukan operasi dereferensi), kadang -kadang dapat dinamai "pointer aman".
Namun, с/с + + juga memiliki gula sintaksis khusus saat mengacu pada sifat objek tanpa dereferensi pointer yang jelas:
Salinan kode adalah sebagai berikut:
obj-> x bukan (*obj) .x
Ideologi ini yang paling erat terkait dengan C ++ dapat dilihat dari implementasi "pointer pintar". Misalnya, di Boost :: shared_ptr, operator penugasan dan copy constructor kelebihan beban, dan penghitung referensi objek juga digunakan untuk menghapus objek melalui GC. Tipe data ini bahkan memiliki nama yang sama - share_ptr.
Implementasi ecmascript
Sekarang kita tahu kebijakan objek yang lewat sebagai parameter dalam ecmascript - lulus dengan berbagi: memodifikasi properti parameter akan mempengaruhi bagian luar, sementara menugaskan kembali nilai tidak akan mempengaruhi objek eksternal. Namun, seperti yang kami sebutkan di atas, pengembang ecmascript umumnya menyebutnya: lulus berdasarkan nilai, kecuali bahwa nilainya adalah salinan dari alamat referensi.
Penemu JavaScript Brendan Ashe juga menulis: Apa yang disahkan adalah salinan referensi (salinan alamat). Jadi apa yang dikatakan semua orang di forum juga benar di bawah penjelasan ini.
Lebih tepatnya, perilaku ini dapat dipahami sebagai tugas sederhana. Kita dapat melihat bahwa internal adalah objek yang sama sekali berbeda, tetapi nilai yang sama dirujuk - yaitu, salinan alamat.
Kode Ecmascript:
Salinan kode adalah sebagai berikut:
var foo = {x: 10, y: 20};
var bar = foo;
peringatan (bar === foo); // BENAR
bar.x = 100;
bar.y = 200;
waspada ([foo.x, foo.y]); // [100, 200]
Artinya, dua pengidentifikasi (name binding) terikat pada objek yang sama dalam memori, dan bagikan objek ini:
Nilai foo: addr (0xff) => {x: 100, y: 200} (alamat 0xff) <= nilai bar: addr (0xff)
Untuk penugasan kembali, ikatan adalah pengidentifikasi objek baru (alamat baru) tanpa mempengaruhi objek yang sebelumnya terikat:
Salinan kode adalah sebagai berikut:
bar = {z: 1, q: 2};
waspada ([foo.x, foo.y]); // [100, 200] Tidak ada perubahan
waspada ([bar.z, bar.q]); // [1, 2] Tetapi sekarang rujukannya adalah objek baru
Artinya, FOO dan BAR sekarang memiliki nilai yang berbeda dan alamat yang berbeda:
Salinan kode adalah sebagai berikut:
Nilai foo: addr (0xff) => {x: 100, y: 200} (alamat 0xff)
Nilai batang: addr (0xfa) => {z: 1, q: 2} (alamat 0xfa)
Izinkan saya menekankan lagi bahwa nilai objek yang disebutkan di sini adalah alamat, bukan struktur objek itu sendiri, dan menetapkan variabel ke variabel lain - referensi ke nilai yang ditetapkan. Oleh karena itu, kedua variabel mengacu pada alamat memori yang sama. Tugas berikutnya adalah alamat baru, yaitu untuk menyelesaikan ikatan ke alamat objek lama dan kemudian mengikat alamat objek baru. Ini adalah perbedaan terpenting antara lulus dengan referensi.
Selain itu, jika kita hanya mempertimbangkan tingkat abstraksi yang disediakan oleh standar ECMA-262, kita hanya melihat konsep "nilai" dalam algoritma, yang mengimplementasikan "nilai" yang dilewati (dapat berupa nilai asli atau objek), tetapi menurut definisi kami di atas, itu juga dapat sepenuhnya disebut "diteruskan dengan nilai" karena alamat referensi juga merupakan nilai.
Namun, untuk menghindari kesalahpahaman (mengapa sifat -sifat objek eksternal dapat diubah di dalam fungsi), rincian tingkat implementasi masih perlu dipertimbangkan di sini - apa yang kita lihat dilewatkan dengan berbagi, atau dengan kata lain - dilewatkan oleh pointer yang aman, yang tidak dapat diterbitkan dan mengubah objek, tetapi nilai properti objek dapat dimodifikasi.
Versi Term
Mari kita tentukan istilah versi kebijakan ini dalam ecmascript.
Ini dapat disebut "Lulus berdasarkan Nilai" - Nilai yang disebutkan di sini adalah kasus khusus, yaitu, nilainya adalah salinan alamat. Dari level ini kita dapat mengatakan: objek dalam ecmascript kecuali pengecualian dilewatkan oleh nilai, yang sebenarnya merupakan tingkat abstraksi ecmascript.
Atau dalam hal ini, secara khusus disebut "disahkan dengan berbagi". Melalui ini, Anda dapat melihat perbedaan antara lewat tradisional dengan nilai dan melewati dengan referensi. Situasi ini dapat dibagi menjadi dua situasi: 1: Nilai asli dilewatkan oleh nilai; 2: Objek dilewatkan dengan berbagi.
Ungkapan "berfungsi dengan jenis referensi" tidak ada hubungannya dengan ecmascript, dan itu salah.
sebagai kesimpulan
Saya harap posting ini membantu mempelajari lebih lanjut detail tingkat makro dan implementasi dalam ecmascript. Seperti biasa, jika ada pertanyaan, jangan ragu untuk mendiskusikannya.