Apa yang tertulis di sini? Di belakang layar objek JavaScript

JavaScript adalah bahasa multi-paradigma yang mendukung pemrograman berorientasi objek dan metode dinamis yang mengikat - konsep yang kuat yang memungkinkan struktur kode JavaScript berubah selama eksekusi program. Ini memberikan peluang serius bagi pengembang, membuat bahasa menjadi fleksibel, tetapi Anda harus membayar semuanya. Dalam hal ini, Anda harus membayar dengan kelengkapan kode. Kontribusi signifikan terhadap harga ini dibuat oleh this , di sekitar perilaku yang telah dikumpulkan banyak hal yang dapat membingungkan programmer.



Penjilidan Metode Dinamis


Pengikatan dinamis memungkinkan Anda menentukan, selama eksekusi program, dan tidak selama kompilasi, metode yang harus dipanggil saat menjalankan perintah tertentu. Dalam JavaScript, mekanisme ini diterapkan menggunakan this dan rantai prototipe. Secara khusus, nilai spesifik this di dalam metode ditentukan saat runtime, dan aturan untuk menentukan nilai ini bervariasi tergantung pada bagaimana metode itu dinyatakan.

Mari kita mainkan satu game. Saya menyebutnya "Apa yang tertulis dalam ini?". Ini pilihan pertamanya - kode modul ES6:

 const a = { a: 'a' }; const obj = { getThis: () => this, getThis2 () {   return this; } }; obj.getThis3 = obj.getThis.bind(obj); obj.getThis4 = obj.getThis2.bind(obj); const answers = [ obj.getThis(), obj.getThis.call(a), obj.getThis2(), obj.getThis2.call(a), obj.getThis3(), obj.getThis3.call(a), obj.getThis4(), obj.getThis4.call(a) ]; 

Sebelum membaca lebih lanjut, pikirkan apa yang akan jatuh ke dalam susunan jawaban dan tuliskan jawabannya. Setelah Anda melakukan ini, uji diri Anda dengan answers larik answers menggunakan console.log() . Apakah Anda berhasil dengan benar “mendekripsi” nilai this di masing-masing kasus?

Kami akan menganalisis masalah ini, dimulai dengan contoh pertama. obj.getThis() mengembalikan undefined . Mengapa Fungsi panah ini tidak dapat diikat. Fungsi seperti this menggunakan this dari ruang lingkup leksikal di sekitarnya. Metode ini disebut dalam modul ES6, dalam lingkup leksikal this undefined akan undefined . Untuk alasan yang sama, undefined mengembalikan panggilan ke obj.getThis.call(a) . Nilai this saat bekerja dengan fungsi panah tidak dapat ditetapkan ulang bahkan dengan .call() atau .bind() . Nilai ini akan selalu sesuai dengan this dari lingkup leksikal, di mana fungsi-fungsi tersebut berada.

Perintah obj.getThis2() menunjukkan cara bekerja dengan this ketika menggunakan metode objek biasa. Jika this tidak terikat dengan metode yang sama, dan asalkan metode ini bukan fungsi panah, yaitu, ia mendukung pengikatan this , kata kunci ini terikat ke objek yang metode ini disebut menggunakan sintaks mengakses properti dari objek melalui dot atau menggunakan kurung kotak.

The obj.getThis2.call(a) konstruk sudah agak sulit untuk obj.getThis2.call(a) . Metode call() memungkinkan Anda untuk memanggil fungsi dengan nilai yang diberikan this , yang ditunjukkan sebagai argumen opsional. Dengan kata lain, dalam hal ini, this diambil dari parameter .call() , sebagai hasilnya, panggilan ke obj.getThis2.call(a) mengembalikan objek a .

Menggunakan perintah obj.getThis3 = obj.getThis.bind(obj); kami mencoba mengikat ke metode this , yang merupakan fungsi panah. Seperti yang telah kita ketahui, ini tidak dapat dilakukan. Akibatnya, panggilan ke obj.getThis3() dan obj.getThis3.call(a) kembali undefined .

Metode yang merupakan fungsi biasa dapat dilampirkan pada this , jadi obj.getThis4() , seperti yang diharapkan, mengembalikan obj . Panggilan ke obj.getThis4.call(a) mengembalikan obj , dan tidak, seperti yang Anda harapkan, a . Faktanya adalah bahwa, sebelum memanggil perintah ini, kita sudah mengikat this dengan perintah obj.getThis4 = obj.getThis2.bind(obj); . Akibatnya, ketika mengeksekusi obj.getThis4.call(a) , status metode yang digunakan setelah pengikatan pertama diperhitungkan diperhitungkan.

Menggunakan ini di kelas


Ini adalah versi kedua dari gim kami - tugas yang sama, tetapi sekarang berdasarkan kelas. Di sini kita menggunakan sintaks untuk mendeklarasikan bidang kelas publik (saat ini, proposal untuk sintaksis ini pada tahap ketiga persetujuan, tersedia secara default di Chrome, Anda dapat menggunakannya dengan @babel/plugin-proposal-class-properties ).

 class Obj { getThis = () => this getThis2 () {   return this; } } const obj2 = new Obj(); obj2.getThis3 = obj2.getThis.bind(obj2); obj2.getThis4 = obj2.getThis2.bind(obj2); const answers2 = [ obj2.getThis(), obj2.getThis.call(a), obj2.getThis2(), obj2.getThis2.call(a), obj2.getThis3(), obj2.getThis3.call(a), obj2.getThis4(), obj2.getThis4.call(a) ]; 

Sebelum membaca lebih lanjut, pikirkan kode tersebut dan tuliskan visi Anda tentang apa yang akan jatuh ke dalam array answers2 .

Apakah kamu sudah selesai?

Di sini, semua pemanggilan metode, kecuali untuk obj2.getThis2.call(a) , akan mengembalikan referensi ke instance objek. Panggilan yang sama akan mengembalikan objek a . Fungsi panah masih mengambil this dari lingkup leksikal. Perbedaan antara contoh ini dan yang sebelumnya adalah perbedaan dalam ruang lingkup dari mana this diambil.

Yaitu, di sini kami bekerja dengan properti kelas, yang menentukan perilaku kode ini.

Faktanya adalah bahwa selama persiapan kode untuk dieksekusi, nilai-nilai ditulis ke properti kelas seperti ini:

 class Obj { constructor() {   this.getThis = () => this; } ... 

Dengan kata lain, ternyata fungsi panah dideklarasikan di dalam konteks fungsi konstruktor. Karena kami bekerja dengan kelas, satu-satunya cara untuk membuat instance adalah menggunakan kata kunci new (jika Anda lupa tentang kata kunci ini, pesan kesalahan akan ditampilkan).

Tugas paling penting yang diselesaikan oleh kata kunci new adalah membuat instance objek baru dan mengikatnya ke konstruktor. Fitur ini, dengan mempertimbangkan apa yang sudah kita bicarakan di bagian sebelumnya, akan membantu Anda memahami apa yang sedang terjadi.

Ringkasan


Sudahkah Anda menyelesaikan tugas yang diuraikan dalam artikel ini? Pemahaman yang baik tentang bagaimana kata kunci this berperilaku dalam JavaScript akan menghemat banyak waktu Anda saat debugging, ketika mencari alasan yang tidak jelas untuk kesalahan yang tidak jelas. Jika Anda menjawab beberapa pertanyaan dengan tidak tepat, itu berarti akan berguna bagi Anda untuk berlatih.

Eksperimen dengan kode sampel, lalu coba sendiri lagi, dan seterusnya, hingga Anda dapat menjawab semua pertanyaan dengan benar. Setelah Anda mengetahuinya sendiri, cari seseorang yang siap mendengarkan Anda dan katakan kepadanya mengapa metode dari tugas mengembalikan apa yang mereka kembalikan.

Jika semua ini menurut Anda lebih rumit dari yang Anda harapkan, ketahuilah bahwa Anda tidak sendirian dalam hal ini. Saya menguji beberapa pengembang untuk mengetahui fitur-fitur this , dan saya pikir hanya satu dari mereka yang benar-benar akurat dalam semua jawaban mereka.

Subsistem bahasa itu, yang pada awalnya tampak seperti pencarian dinamis untuk metode yang dapat dipengaruhi menggunakan .call() , .bind() atau .apply() , mulai terlihat jauh lebih rumit setelah kemunculan fungsi panah dan kelas.

Tampaknya, akan berguna untuk mencatat fitur utama fungsi kelas dan panah dalam hal menggunakan this . Ingat bahwa fungsi panah selalu menggunakan this dari ruang lingkup leksikal mereka, dan this di kelas, pada kenyataannya, terikat dengan fungsi konstruktor kelas. Dan jika Anda pernah merasa bahwa Anda tidak tahu persis apa artinya this , gunakan debugger untuk memeriksa asumsi Anda tentang ini.

Juga, ingatlah bahwa Anda dapat melakukan banyak hal dalam JavaScript tanpa menggunakan this dalam kode Anda. Pengalaman memberi tahu saya bahwa hampir semua kode JS dapat ditulis ulang dalam bentuk fungsi murni yang menerima semua argumen yang bekerja dengannya, dalam bentuk daftar parameter yang ditentukan secara eksplisit ( this dapat diartikan sebagai parameter yang ditentukan secara implisit dengan keadaan yang bisa berubah-ubah). Logika yang terkandung dalam fungsi murni bersifat deterministik, yang meningkatkan kemampuan uji mereka. Fungsi-fungsi seperti itu tidak memiliki efek samping, yang berarti bahwa ketika bekerja dengannya, tidak seperti memanipulasi this , Anda tidak mungkin “menghancurkan” apa pun di luarnya. Setiap kali Anda mengubah this , Anda dihadapkan dengan masalah potensial, yaitu bahwa sesuatu yang bergantung pada this dapat berhenti bekerja dengan benar.

Meskipun demikian, perlu dicatat bahwa this adalah konsep yang berguna. Sebagai contoh, ini dapat diterapkan untuk mengatur pembagian metode tertentu dengan banyak objek. Bahkan dalam pemrograman fungsional, this bisa berguna untuk memanggil metode lain dari satu metode objek, yang memungkinkan Anda untuk membuat sesuatu yang baru berdasarkan konstruksi yang ada.



Source: https://habr.com/ru/post/id455527/


All Articles