Tingkatkan pengetahuan JavaScript Anda dengan mem-parsing kode sumber

Ketika Anda memulai karir pemrograman Anda, menggali kode sumber perpustakaan terbuka dan kerangka kerja mungkin tampak sedikit menakutkan. Dalam artikel ini, Karl Mungazi berbagi pengalamannya tentang bagaimana ia mengatasi rasa takutnya dan mulai menggunakan kode sumber untuk memperoleh pengetahuan dan mengembangkan keterampilan. Dia juga menggunakan Redux untuk menunjukkan bagaimana dia "mem-parsing" perpustakaan.

Apakah Anda ingat ketika Anda pertama kali membenamkan diri dalam kode perpustakaan atau kerangka kerja yang sering Anda gunakan? Dalam hidup saya, momen ini datang pada pekerjaan pertama saya sebagai pengembang front-end tiga tahun lalu.

Kami hanya menulis ulang kerangka kerja lama yang sudah usang yang digunakan untuk membuat kursus pelatihan interaktif. Pada awal penulisan ulang, kami melihat beberapa solusi turnkey, termasuk Mithril, Inferno, Angular, React, Aurelia, Vue, dan Polymer. Karena saya masih Padawan muda (yang baru saja beralih dari jurnalisme ke pengembangan web), saya sangat takut akan kompleksitas dari setiap kerangka kerja dan kurangnya pemahaman tentang bagaimana mereka bekerja.

Pemahaman mulai muncul ketika saya mulai hati-hati mengeksplorasi kerangka Mithril. Sejak itu, pengetahuan saya tentang JavaScript - dan pemrograman pada umumnya - telah sangat diperkuat berkat waktu yang dihabiskan untuk menggali internal perpustakaan, yang saya gunakan setiap hari di tempat kerja dan di proyek saya sendiri. Pada artikel ini saya akan memberi tahu Anda bagaimana Anda dapat menggunakan perpustakaan favorit Anda sebagai tutorial

gambar
Saya mulai membaca kode sumber dengan fungsi hyperscript dari Mithril

Pro parsing kode sumber


Salah satu keuntungan utama dari parsing source code adalah Anda dapat belajar banyak. Ketika saya mulai menguraikan kode Mithril, saya memiliki ide yang sangat buruk tentang apa DOM virtual itu. Ketika saya selesai, saya sudah tahu bahwa DOM virtual adalah teknik yang melibatkan pembuatan pohon objek yang menggambarkan antarmuka pengguna. Pohon ini kemudian dapat dikonversi ke elemen DOM menggunakan API DOM seperti document.createElement. Untuk memperbarui, pohon baru dibuat yang menggambarkan keadaan antarmuka di masa depan dan kemudian dibandingkan dengan versi sebelumnya dari pohon ini.

Saya membaca tentang ini di banyak artikel dan manual, tetapi yang paling bermanfaat adalah mengamati semua ini saat mengerjakan aplikasi kita. Saya juga belajar mengajukan pertanyaan yang tepat saat membandingkan kerangka kerja. Alih-alih membandingkan peringkat, misalnya, Anda dapat mengajukan pertanyaan "Bagaimana cara kerja kerangka kerja ini dengan perubahan memengaruhi kinerja dan kenyamanan pengguna akhir?"

Kelebihan lainnya adalah pengembangan pemahaman arsitektur aplikasi yang baik. Terlepas dari kenyataan bahwa sebagian besar proyek open-source umumnya kurang lebih memiliki struktur yang mirip dengan repositori mereka, mereka masih memiliki perbedaan. Struktur Mithril sangat datar dan jika Anda berpengalaman dalam API-nya, Anda dapat membuat asumsi yang cukup realistis tentang kode dalam render, router dan folder permintaan. Struktur React, di sisi lain, mencerminkan arsitektur barunya. Pengembang memisahkan modul yang bertanggung jawab untuk memperbarui UI (react-reconciler) dari modul yang bertanggung jawab untuk merender elemen DOM (react-dom).

Salah satu keuntungan pemisahan ini bagi pengembang adalah mereka dapat menulis penyaji mereka sendiri menggunakan kait di rekonsiliasi-reaksi. Parcel, pembuat modul yang baru saya pelajari, juga memiliki folder paket, seperti React. Modul utama disebut paket-bundler, berisi kode yang bertanggung jawab untuk membuat rakitan, pengoperasian server pembaruan modul (server modul panas), dan alat baris perintah.

gambar
Parsing kode sumber segera membuat Anda membaca spesifikasi JavaScript.

Kelebihan lainnya, yang merupakan kejutan besar bagi saya, adalah membuatnya lebih mudah bagi Anda untuk membaca spesifikasi JavaScript resmi. Pertama kali saya menoleh padanya ketika saya mencoba mencari tahu apa perbedaan antara throw Error dan throw new Error (spoiler - nothing ). Saya mengajukan pertanyaan ini karena Mithril menggunakan throw Error dalam implementasi fungsi m dan saya bertanya-tanya mengapa itu lebih baik daripada membuang Error baru. Kemudian saya juga mengetahui bahwa operator && dan || belum tentu mengembalikan nilai Boolean , saya menemukan aturan yang digunakan operator perbandingan non-ketat == "menyelesaikan" nilai dan alasan mengapa Object.prototype.toString.call ({}) mengembalikan '[objek objek]'.

Cara mengurai kode sumber


Ada banyak cara untuk mengurai kode sumber. Menurut saya, cara termudah adalah: pilih metode dari perpustakaan Anda dan jelaskan apa yang terjadi ketika Anda memanggilnya. Tidak perlu menggambarkan setiap langkah, Anda hanya perlu mencoba memahami prinsip dan struktur umumnya.

Baru-baru ini, saya mem-parsing ReactDOM.render dengan cara ini dan belajar banyak tentang React Fiber dan beberapa kesulitan dalam mengimplementasikannya. Untungnya, React sangat populer dan kehadiran sejumlah besar artikel tentang topik yang sama dari pengembang lain mempercepat prosesnya.

Menyelam ke dalam kode ini juga memperkenalkan konsep penjadwalan koperasi , metode window.requestIdleCallback, dan contoh langsung dari daftar tertaut (Bereaksi proses pembaruan dengan mengirimkannya ke antrian, yang merupakan daftar pembaruan terkait terkait). Dalam prosesnya, akan menyenangkan untuk membuat aplikasi sederhana menggunakan perpustakaan. Ini membuat proses debug lebih mudah karena Anda tidak harus berurusan dengan jejak tumpukan perpustakaan lain.

Jika saya tidak melakukan review terperinci, maka saya akan membuka folder node_modules dalam proyek yang sedang saya kerjakan atau melihat GitHub. Saya selalu melakukan ini ketika menemukan bug atau fitur menarik. Saat membaca kode di GitHub, pastikan ini adalah versi terbaru. Kode versi terbaru dapat dilihat dengan mengklik tombol untuk mengubah cabang dan memilih "tag". Perubahan pustaka dan kerangka kerja sedang berlangsung, jadi Anda tidak mungkin ingin menguraikan sesuatu yang mungkin tidak ada di versi berikutnya.

Versi yang lebih dangkal dari kode sumber belajar adalah apa yang saya sebut "tampilan cepat". Entah bagaimana saya menginstal express.js, membuka folder node_modules dan melalui dependensi. Jika README tidak memberi saya penjelasan yang memuaskan, saya membaca sumbernya. Ini membawa saya ke penemuan menarik:

  • Express menggunakan dua modul untuk menggabungkan objek, dan pengoperasian modul ini sangat berbeda. penggabung-deskriptor hanya menambahkan properti yang ditemukan di objek sumber, dan juga menambahkan properti non-enumerable, sedangkan utils-merge membahas properti yang disebutkan dari objek dan seluruh rantai prototipe. penggabung-deskriptor menggunakan Object.getOwnPropertyNames () dan Object.getOwnPropertyDescriptor (), dan utils-merge uses for..in;
  • Modul setprototypeof menyediakan opsi lintas platform untuk menentukan prototipe objek yang dibuat (dipakai);
  • escape-html adalah modul pelolosan string 78-baris, setelah itu kontennya dapat dimasukkan ke dalam HTML;

Meskipun penemuan-penemuan ini kemungkinan besar tidak berguna segera, pemahaman umum tentang dependensi perpustakaan atau kerangka kerja Anda sangat membantu.

Alat peramban debugging adalah teman terbaik Anda ketika men-debug kode di frontend. Di antara hal-hal lain, mereka memungkinkan Anda untuk menghentikan program kapan saja dan memeriksa pada saat yang sama statusnya, melewatkan fungsi, atau masuk atau keluar. Dalam kode yang diperkecil, ini tidak mungkin - itu sebabnya saya membongkar kode ini dan memasukkannya ke file yang sesuai di folder node_modules.

gambar
Gunakan debugger sebagai aplikasi yang bermanfaat. Buat asumsi, lalu uji.

Studi kasus: menghubungkan fungsi di Redux


React-Redux adalah perpustakaan untuk mengelola keadaan aplikasi Bereaksi. Ketika saya bekerja dengan perpustakaan populer seperti ini, saya mulai dengan mencari artikel tentang penggunaannya. Dalam mempersiapkan contoh ini, saya meninjau artikel ini. Ini adalah nilai tambah lain dalam mempelajari kode sumber - ini akan mengarahkan Anda ke artikel informatif seperti ini yang meningkatkan pemikiran dan pemahaman Anda.

Connect adalah fungsi react-redux yang menghubungkan komponen reaksi dan toko redux suatu aplikasi. Bagaimana? Menurut dokumentasi, dia melakukan hal berikut:
"... mengembalikan kelas komponen baru yang terkait, yang merupakan pembungkus komponen yang diteruskan ke sana."
Setelah membaca ini, saya mengajukan pertanyaan berikut:

  • Apakah saya tahu pola atau konsep di mana fungsi mengembalikan parameter input yang dibungkus dengan fungsionalitas tambahan?
  • Jika demikian, bagaimana saya menggunakan ini berdasarkan deskripsi dari dokumentasi?

Biasanya langkah selanjutnya adalah membuat aplikasi primitif menggunakan fungsi connect. Namun demikian, dalam situasi ini, saya menggunakan aplikasi baru pada React, yang kami kerjakan, karena saya ingin memahami terhubung dalam konteks aplikasi yang kemungkinan besar akan segera sampai ke produksi.

Komponen yang saya fokuskan terlihat seperti ini:

class MarketContainer extends Component { // code omitted for brevity } const mapDispatchToProps = dispatch => { return { updateSummary: (summary, start, today) => dispatch(updateSummary(summary, start, today)) } } export default connect(null, mapDispatchToProps)(MarketContainer); 

Ini adalah komponen wadah yang berfungsi sebagai pembungkus untuk empat komponen terkait yang lebih kecil. Salah satu hal pertama yang Anda temukan dalam file yang menghubungkan ekspor adalah komentar "terhubung adalah fasad untuk connectAdvanced". Sudah pada tahap ini kita dapat belajar sesuatu: kita memiliki kesempatan untuk mengamati pola "fasad" dalam aksi. Di akhir file, kita melihat koneksi mengekspor panggilan ke fungsi createConnect. Parameternya adalah seperangkat nilai default yang direstrukturisasi sebagai berikut:

 export function createConnect({ connectHOC = connectAdvanced, mapStateToPropsFactories = defaultMapStateToPropsFactories, mapDispatchToPropsFactories = defaultMapDispatchToPropsFactories, mergePropsFactories = defaultMergePropsFactories, selectorFactory = defaultSelectorFactory } = {}) 

Dan kami memiliki satu momen instruktif lagi: ekspor fungsi yang disebut dan perusakan argumen fungsi secara default. Restrukturisasi bermanfaat bagi kita karena kode dapat ditulis seperti ini:

 export function createConnect({ connectHOC = connectAdvanced, mapStateToPropsFactories = defaultMapStateToPropsFactories, mapDispatchToPropsFactories = defaultMapDispatchToPropsFactories, mergePropsFactories = defaultMergePropsFactories, selectorFactory = defaultSelectorFactory }) 

Akibatnya, kami akan mendapatkan kesalahan - UnEught TypeError: Tidak dapat merusak properti 'connectHOC' dari 'undefined' atau 'null'. Ini akan terjadi karena fungsi tidak memiliki nilai argumen default.

Catatan: untuk lebih memahami restrukturisasi argumen, Anda dapat membaca artikel David Walsh . Beberapa poin mungkin tampak sepele, tergantung pada pengetahuan Anda tentang bahasa - maka Anda dapat fokus pada poin-poin yang Anda tidak kenal.

Fungsi createConnect sendiri tidak melakukan apa-apa. Itu hanya mengembalikan fungsi koneksi yang saya gunakan di sini:

 export default connect(null, mapDispatchToProps)(MarketContainer) 

Dibutuhkan empat argumen opsional dan tiga di antaranya melewati fungsi kecocokan , yang membantu menentukan perilaku mereka berdasarkan argumen yang dilewati, serta jenisnya. Ternyata karena argumen kedua yang dilewatkan untuk mencocokkan adalah salah satu dari tiga fungsi yang diimpor ke connect, saya harus memilih ke mana harus pergi berikutnya.

Ada juga sesuatu untuk dipelajari dari fungsi proxy yang digunakan untuk membungkus argumen pertama dalam koneksi, jika argumen ini adalah fungsi; dari utilitas isPlainObject yang digunakan untuk memeriksa objek biasa atau dari modul peringatan , yang menunjukkan bagaimana Anda dapat membuat debugger yang akan memecah semua kesalahan. Setelah fungsi pertandingan, kami beralih ke connectHOC, fungsi yang mengambil komponen reaksi kami dan menghubungkannya dengan redux. Ada panggilan fungsi lain yang mengembalikan wrapWithConnect - fungsi yang benar-benar menangani pengikatan komponen ke repositori.

Melihat implementasi connectHOC, saya bisa menebak mengapa detail implementasi connect harus disembunyikan. Ini pada dasarnya adalah jantung dari reaksi-redux dan mengandung logika yang seharusnya tidak dapat diakses melalui koneksi. Bahkan jika kita memikirkan ini, maka nanti, jika kita perlu menggali lebih dalam, kita akan sudah memiliki bahan sumber dengan penjelasan rinci tentang kode.

Ringkaslah


Mempelajari kode sumber sangat rumit pada awalnya. Tapi, seperti yang lainnya, itu menjadi lebih mudah seiring waktu. Tugasnya bukan untuk memahami segalanya, tetapi untuk mengeluarkan sesuatu yang berguna untuk dirinya sendiri - pemahaman bersama dan pengetahuan baru. Sangat penting untuk berhati-hati selama seluruh proses dan mempelajari rinciannya.

Sebagai contoh, saya menemukan fungsi isPlainObject menarik karena menggunakan ini if โ€‹โ€‹(typeof obj! == 'object' || obj === null) mengembalikan false untuk memastikan bahwa argumen yang diteruskan adalah objek sederhana. Ketika saya pertama kali membaca kode ini, saya berpikir, mengapa tidak hanya menggunakan Object.prototype.toString.call (opts)! == '[objek objek]', yang akan mengurangi kode dan objek yang terpisah dari subtipe mereka seperti Tanggal. Tetapi sudah di baris berikutnya jelas bahwa bahkan jika tiba-tiba (tiba-tiba!) Seorang pengembang menggunakan menghubungkan mengembalikan objek Date, misalnya, memeriksa Object.getPrototypeOf (obj) === null dapat menangani ini.

Poin tak terduga lainnya di isPlainObject di tempat ini:

 while (Object.getPrototypeOf(baseProto) !== null) { baseProto = Object.getPrototypeOf(baseProto) } 

Menemukan jawaban di Google membawa saya ke utas ini di StackOverflow, dan komentar ini di Redux GitHub, yang menjelaskan bagaimana kode ini menangani situasi di mana, misalnya, objek ditransfer dari iFrame.

-

Pertama memutuskan untuk menerjemahkan artikel tersebut. Saya akan berterima kasih atas klarifikasi, saran dan rekomendasi

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


All Articles