JavaScript Di Bawah Terpal

Nov 28 2022
Daftar Isi Dalam artikel ini, kita akan mendalami cara kerja bagian dalam JavaScript dan cara kerjanya yang sebenarnya. Dengan memahami detailnya, Anda akan memahami perilaku kode Anda dan karena itu Anda dapat menulis aplikasi yang lebih baik.

Daftar isi

  • Utas dan Tumpukan Panggilan
  • Konteks Eksekusi
  • Loop Peristiwa dan JavaScript Asinkron
  • Penyimpanan Memori dan Pengumpulan Sampah
  • Kompilasi JIT (Just In Time).
  • Ringkasan

Pada artikel ini, kita akan mendalami cara kerja bagian dalam JavaScript dan bagaimana JavaScript sebenarnya berjalan. Dengan memahami detailnya, Anda akan memahami perilaku kode Anda dan karena itu Anda dapat menulis aplikasi yang lebih baik.

JavaScript digambarkan sebagai:

Bahasa pemrograman single-threaded, garbage-collected, interpreted, atau Just In Time dikompilasi dengan event loop non-blocking.

Mari kita bongkar masing-masing istilah kunci ini.

Utas & Tumpukan Panggilan:

Mesin JavaScript adalah penerjemah single-threaded yang terdiri dari heap dan stack panggilan tunggal yang digunakan untuk menjalankan program.
Tumpukan panggilan adalah struktur data yang menggunakan prinsip Last In, First Out (LIFO) untuk menyimpan dan mengelola pemanggilan fungsi (panggilan) sementara.
Ini berarti bahwa fungsi terakhir yang didorong ke dalam tumpukan adalah yang pertama keluar saat fungsi kembali.
Karena tumpukan panggilan tunggal, eksekusi fungsi dilakukan satu per satu, dari atas ke bawah. Ini berarti tumpukan panggilan sinkron.

Sekarang, karena sinkron, Anda akan bertanya-tanya bagaimana JavaScript menangani panggilan asinkron?
Nah, event loop adalah rahasia di balik pemrograman asinkron JavaScript.
Namun sebelum menjelaskan konsep panggilan async dalam JavaScript dan bagaimana mungkin dengan bahasa single-threaded mari kita pahami dulu bagaimana kode dieksekusi.

Konteks Eksekusi (EC):

Konteks Eksekusi didefinisikan sebagai lingkungan tempat kode JavaScript dieksekusi.
Penciptaan Konteks Eksekusi terjadi dalam dua fase:

1. Fase Pembuatan Memori:

  • Membuat objek global (yang disebut objek jendela di browser dan objek global di NodeJS).
  • Membuat objek "ini" dan mengikatnya ke objek global.
  • Menyiapkan tumpukan memori (Tumpukan adalah wilayah memori yang besar, sebagian besar tidak terstruktur) untuk menyimpan referensi variabel dan fungsi.
  • Menyimpan fungsi dan variabel dalam konteks eksekusi global dengan mengimplementasikan Hoisting .

Sekarang setelah kita mengetahui langkah-langkah di balik eksekusi kode, mari kita kembali ke

Putaran Acara:

Pertama, mari kita mulai dengan melihat diagram ini:

Putaran Acara di JS

Kami memiliki mesin yang terdiri dari dua komponen utama:
* Memory Heap — ini adalah tempat terjadinya alokasi memori.
* Panggil Stack — ini adalah tempat bingkai tumpukan Anda saat kode Anda dijalankan.

Kami memiliki API Web yang merupakan utas yang tidak dapat Anda akses, Anda cukup meneleponnya. Mereka adalah bagian dari browser tempat konkurensi dimulai, seperti DOM, AJAX, setTimeout, dan banyak lagi.

Terakhir, ada antrian Callback yang merupakan daftar event yang akan diproses. Setiap acara memiliki fungsi terkait yang dipanggil untuk menanganinya.

Jadi apa tugas event loop di sini?
Event Loop memiliki satu tugas sederhana — untuk memantau Call Stack dan Callback Queue. Jika Call Stack kosong, Event Loop akan mengambil event pertama dari antrian dan akan mendorongnya ke Call Stack, yang menjalankannya secara efektif.
Iterasi seperti itu disebut tanda centang di Event Loop. Setiap acara hanyalah panggilan balik fungsi.

Penyimpanan Memori dan Pengumpulan Sampah:

Untuk memahami kebutuhan pengumpulan sampah, pertama-tama kita harus memahami Siklus Hidup Memori yang hampir sama untuk bahasa pemrograman apa pun, ia memiliki 3 langkah utama.
1. Alokasikan memori.
2. Gunakan memori yang dialokasikan untuk membaca atau menulis atau keduanya.
3. Lepaskan memori yang dialokasikan saat tidak diperlukan lagi.

Sebagian besar masalah manajemen memori terjadi saat kami mencoba melepaskan memori yang dialokasikan. Perhatian utama yang muncul adalah penentuan sumber daya memori yang tidak terpakai.
Dalam kasus bahasa tingkat rendah di mana pengembang harus memutuskan secara manual kapan memori tidak lagi diperlukan, bahasa tingkat tinggi seperti JavaScript menggunakan bentuk manajemen memori otomatis yang dikenal sebagai Pengumpulan Sampah (GC).
JavaScript menggunakan dua strategi terkenal untuk melakukan GC: teknik penghitungan Referensi dan algoritma Mark-and-sweep.
Berikut penjelasan mendetail dari MDN tentang kedua algoritme dan cara kerjanya.

Kompilasi JIT (Just In Time):

Mari kita kembali ke definisi JavaScript: Dikatakan "Diterjemahkan, bahasa pemrograman yang dikompilasi JIT", jadi apa artinya itu? Bagaimana kalau memulai dengan perbedaan antara kompiler dan juru bahasa secara umum?

Sebagai analogi, pikirkan dua orang dengan bahasa berbeda yang ingin berkomunikasi. Mengkompilasi seperti berhenti dan menghabiskan seluruh waktu untuk mempelajari bahasa, dan menafsirkan akan seperti meminta seseorang di sana untuk menafsirkan setiap kalimat.

Jadi bahasa yang dikompilasi memiliki waktu tulis yang lambat dan waktu proses yang cepat dan bahasa yang ditafsirkan memiliki kebalikannya.

Berbicara dengan istilah teknis: kompilasi adalah proses menutupi kode sumber program menjadi kode biner yang dapat dibaca mesin, sebelum eksekusi, dan kompiler mengambil seluruh program sekaligus.

Di sisi lain, seorang juru bahasa adalah sebuah program, yang mengeksekusi instruksi program tanpa memerlukannya untuk dikompilasi sebelumnya ke dalam format yang dapat dibaca mesin, dan membutuhkan satu baris kode pada satu waktu.

Dan inilah peran kompilasi JIT yang meningkatkan kinerja program yang ditafsirkan. Seluruh kode diubah menjadi kode mesin sekaligus dan kemudian dieksekusi segera .

Di dalam kompiler JIT, kami memiliki komponen baru yang disebut monitor (alias profiler). Monitor itu mengawasi kode saat dijalankan dan

  • Identifikasi komponen kode yang panas atau hangat, misalnya: kode berulang.
  • Transfrom komponen-komponen itu menjadi kode mesin selama run-time.
  • Optimalkan kode mesin yang dihasilkan.
  • Hot swap implementasi kode sebelumnya.

Sekarang setelah kita memahami konsep intinya, mari luangkan waktu sebentar untuk menyatukan semuanya dan meringkas langkah-langkah yang diikuti JS Engine saat mengeksekusi kode:

Sumber gambar: melintasi saluran media
  1. Mesin JS mengambil kode JS yang ditulis dalam sintaks yang dapat dibaca manusia dan mengubahnya menjadi kode mesin.
  2. Mesin menggunakan parser untuk menelusuri kode baris demi baris dan memeriksa apakah sintaksnya benar. Jika ada kesalahan, kode akan berhenti dieksekusi dan kesalahan akan terjadi.
  3. Jika semua pemeriksaan lulus, parser membuat struktur data pohon yang disebut Pohon Sintaks Abstrak (AST).
  4. AST adalah struktur data yang mewakili kode dalam struktur seperti pohon. Lebih mudah mengubah kode menjadi kode mesin dari AST.
  5. Penerjemah kemudian mengambil AST dan mengubahnya menjadi IR, yang merupakan abstraksi dari kode mesin dan perantara antara kode JS dan kode mesin. IR juga memungkinkan untuk melakukan pengoptimalan dan lebih mobile.
  6. Kompiler JIT kemudian mengambil IR yang dihasilkan dan mengubahnya menjadi kode mesin, dengan mengkompilasi kode, mendapatkan umpan balik dengan cepat dan menggunakan umpan balik tersebut untuk meningkatkan proses kompilasi.

Terima kasih telah membaca :)

Anda dapat mengikuti saya di Twitter dan LinkedIn .