Apa ini di sini? Operasi Batin Objek JavaScript


Foto: Curious Liliana Saeb (CC BY 2.0)


JavaScript adalah bahasa multi-paradigma yang mendukung pemrograman berorientasi objek dan tautan dinamis. Tautan dinamis adalah konsep yang kuat yang memungkinkan Anda mengubah struktur kode JavaScript saat runtime, tetapi daya tambahan dan fleksibilitas ini diperoleh dengan mengorbankan beberapa kebingungan, yang sebagian besar terkait dengan perilaku this dalam JavaScript.


Tautan dinamis


Dengan pengikatan dinamis, definisi metode untuk memanggil terjadi pada waktu berjalan, dan bukan pada waktu kompilasi. JavaScript melakukan ini dengan this dan rantai prototipe. Secara khusus, di dalam metode, this ditentukan selama panggilan, dan nilai this akan berbeda tergantung pada bagaimana metode itu didefinisikan.


Ayo main game. Saya memanggilnya "Apa this sini?"


 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) ]; 

Pikirkan tentang apa nilai dalam array answers dan periksa jawaban Anda dengan console.log() . Tertebak?


Mari kita mulai dengan kasing pertama dan melanjutkan secara berurutan. obj.getThis() mengembalikan undefined , tetapi mengapa? Fungsi panah tidak pernah memiliki this sendiri. Sebagai gantinya, mereka selalu mengambil this dari ruang lingkup leksikal ( kira-kira Leksikal ini ). Untuk root dari modul ES6, wilayah leksikal akan memiliki nilai yang undefined . obj.getThis.call(a) juga tidak didefinisikan untuk alasan yang sama. Untuk fungsi panah, this tidak dapat diganti, bahkan dengan .call() atau .bind() . this akan selalu diambil dari domain leksikal.


obj.getThis2() mendapatkan pengikatan selama pemanggilan metode. Jika tidak ada pengikatan ini untuk fungsi ini sebelumnya, maka ini dapat terikat this (karena ini bukan fungsi panah). Dalam hal ini, this adalah objek obj yang terikat pada saat metode dipanggil . atau sintaks akses properti [squareBracket] . ( perhatikan penjilidan implisit )


obj.getThis2.call(a) sedikit lebih rumit. Metode call() memanggil fungsi dengan nilai yang diberikan ini dan argumen opsional. Dengan kata lain, metode mendapat pengikatan this dari parameter .call() , jadi obj.getThis2.call(a) mengembalikan objek a . ( perhatikan penjilidan eksplisit )


Dalam kasus obj.getThis3 = obj.getThis.bind(obj); kami mencoba untuk mendapatkan fungsi panah dengan ikatan this , yang, seperti yang telah kami ketahui, tidak akan berfungsi, jadi kami mendapatkan undefined untuk obj.getThis3() dan obj.getThis3.call(a) masing-masing.


Untuk metode reguler, Anda dapat mengikat, jadi obj.getThis4() mengembalikan obj , seperti yang diharapkan. Dia sudah mendapatkan ikatannya di sini obj.getThis4 = obj.getThis2.bind(obj); , dan obj.getThis4.call(a) memperhitungkan binding pertama dan mengembalikan obj bukan a .


Bola bengkok


Kami akan memecahkan masalah yang sama, tetapi kali ini kami menggunakan class dengan bidang publik untuk menjelaskan objek ( inovasi Tahap 3 pada saat penulisan ini tersedia di Chrome secara default dan dengan properti @babel/plugin-offer-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) ]; 

Pikirkan jawaban sebelum melanjutkan.


Apakah kamu siap


Semua panggilan kecuali obj2.getThis2.call(a) mengembalikan instance objek. obj2.getThis2.call(a) mengembalikan a . Fungsi panah masih mendapatkan this dari lingkungan leksikal mereka. Ada perbedaan dalam bagaimana this dari lingkungan leksikal didefinisikan untuk properti kelas. Di dalam, inisialisasi properti kelas terlihat seperti ini:


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

Dengan kata lain, fungsi panah didefinisikan dalam konteks konstruktor. Karena ini adalah kelas, satu-satunya cara untuk membuat turunan adalah dengan menggunakan kata kunci new (menghilangkan yang new akan menghasilkan kesalahan). Salah satu hal terpenting yang dilakukan kata kunci new adalah membuat instance objek baru dan mengikatnya ke konstruktor. Perilaku ini, dikombinasikan dengan perilaku lain yang kami sebutkan di atas, harus menjelaskan sisanya.


Kesimpulan


Apakah kamu berhasil? Pemahaman yang baik tentang bagaimana this berlaku dalam JavaScript akan menghemat banyak waktu untuk debugging masalah yang kompleks. Jika Anda membuat kesalahan dalam jawaban, ini berarti Anda perlu sedikit berlatih. Berlatihlah dengan contoh, lalu kembali dan uji diri Anda lagi sampai Anda dapat menjalankan tes dan menjelaskan kepada orang lain mengapa metode mengembalikan apa yang mereka kembalikan.


Jika itu lebih sulit dari yang Anda harapkan, maka Anda tidak sendirian. Saya bertanya banyak pengembang tentang topik ini dan saya pikir sejauh ini hanya satu dari mereka yang berhasil mengatasi tugas ini.


Apa yang dimulai sebagai pencarian metode dinamis yang dapat Anda redirect dengan .call() , .bind() atau .apply() telah menjadi jauh lebih rumit dengan penambahan metode kelas dan fungsi panah. Mungkin Anda harus sekali lagi fokus pada ini. Ingat bahwa fungsi panah selalu mengambil this dari lingkup leksikal, dan class this sebenarnya dibatasi secara leksikal oleh konstruktor kelas di bawah tenda. Jika Anda meragukan this , ingatlah bahwa Anda dapat menggunakan debugger untuk memeriksa nilainya.


Ingatlah bahwa dalam menyelesaikan banyak tugas JavaScript Anda dapat melakukannya tanpa this . Dalam pengalaman saya, hampir semuanya dapat didefinisikan ulang dalam hal fungsi murni yang mengambil semua argumen yang digunakan sebagai parameter eksplisit ( this dapat dianggap sebagai variabel implisit). Logika yang dijelaskan melalui fungsi murni bersifat deterministik, yang membuatnya lebih dapat diuji. Juga, dengan pendekatan ini, tidak ada efek samping, oleh karena itu, tidak seperti saat memanipulasi this , Anda tidak mungkin memecahkan apa pun. Setiap kali this diatur, sesuatu yang tergantung pada nilainya mungkin rusak.


Namun, terkadang this berguna. Misalnya, untuk bertukar metode antara sejumlah besar objek. Bahkan dalam pemrograman fungsional, this dapat digunakan untuk mengakses metode objek lain untuk mengimplementasikan transformasi aljabar yang diperlukan untuk membangun aljabar baru di atas yang sudah ada. Jadi, universal .flatMap() dapat diperoleh dengan menggunakan this.map() dan this.constructor.of() .




Terima kasih atas bantuannya menerjemahkan wksmirnowa dan VIBaH_dev

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


All Articles