
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