Jika Anda seorang pengembang JavaScript atau ingin menjadi pengembang JavaScript, ini berarti Anda perlu memahami mekanisme internal untuk mengeksekusi kode JS. Secara khusus, pemahaman tentang apa konteks eksekusi dan tumpukan panggilan, mutlak diperlukan untuk menguasai konsep JavaScript lainnya, seperti meningkatkan variabel, cakupan, dan penutupan. Bahan, terjemahan yang kami terbitkan hari ini, dikhususkan untuk konteks eksekusi dan tumpukan panggilan dalam JavaScript.

Konteks eksekusi
Konteks eksekusi adalah, dalam istilah yang disederhanakan, sebuah konsep yang menggambarkan lingkungan di mana kode JavaScript dieksekusi. Kode selalu dijalankan di dalam konteks.
β Jalankan tipe konteks
JavaScript memiliki tiga jenis konteks eksekusi:
- Konteks eksekusi global. Ini adalah konteks eksekusi standar dasar. Jika beberapa kode tidak ada di dalam fungsi apa pun, maka kode ini milik konteks global. Konteks global dicirikan oleh keberadaan objek global, yang, dalam kasus browser, adalah objek
window
, dan fakta bahwa this
menunjuk ke objek global ini. Suatu program hanya dapat memiliki satu konteks global. - Konteks pelaksanaan fungsi. Setiap kali fungsi dipanggil, konteks baru dibuat untuk itu. Setiap fungsi memiliki konteks eksekusi sendiri. Suatu program dapat secara bersamaan memiliki banyak konteks untuk menjalankan fungsi. Saat membuat konteks baru untuk eksekusi suatu fungsi, ia akan melalui urutan langkah-langkah tertentu, yang akan kita bahas di bawah ini.
- Konteks eksekusi dari fungsi
eval
. Kode yang dieksekusi di dalam fungsi eval
juga memiliki konteks eksekusi sendiri. Namun, fungsi eval
jarang digunakan, jadi di sini kita tidak akan berbicara tentang konteks eksekusi ini.
Tumpukan eksekusi
Tumpukan eksekusi, yang juga disebut tumpukan panggilan, adalah tumpukan LIFO yang digunakan untuk menyimpan konteks eksekusi yang dibuat selama eksekusi kode.
Ketika mesin JS mulai memproses skrip, mesin membuat konteks eksekusi global dan menempatkannya di tumpukan saat ini. Ketika suatu perintah untuk memanggil suatu fungsi terdeteksi, mesin menciptakan konteks eksekusi baru untuk fungsi ini dan menempatkannya di atas tumpukan.
Mesin melakukan fungsi yang konteks eksekusi berada di bagian atas tumpukan. Ketika fungsi selesai, konteksnya dihapus dari tumpukan dan kontrol ditransfer ke konteks yang ada di elemen tumpukan sebelumnya.
Kami akan mengeksplorasi ide ini dengan contoh berikut:
let a = 'Hello World!'; function first() { console.log('Inside first function'); second(); console.log('Again inside first function'); } function second() { console.log('Inside second function'); } first(); console.log('Inside Global Execution Context');
Begini cara tumpukan panggilan akan berubah ketika kode ini dijalankan.
Status Stack PanggilanKetika kode di atas dimuat ke browser, mesin JavaScript membuat konteks eksekusi global dan menempatkannya di tumpukan panggilan saat ini. Saat melakukan panggilan ke fungsi
first()
, mesin membuat konteks baru untuk fungsi ini dan menempatkannya di bagian atas tumpukan.
Ketika fungsi
second()
dipanggil dari fungsi
first()
, konteks eksekusi baru dibuat untuk fungsi ini dan juga didorong ke stack. Setelah fungsi
second()
menyelesaikan pekerjaannya, konteksnya dihapus dari tumpukan dan kontrol ditransfer ke konteks eksekusi yang terletak di tumpukan di bawahnya, yaitu, ke konteks fungsi
first()
.
Ketika fungsi
first()
keluar, konteksnya muncul dari tumpukan dan kontrol ditransfer ke konteks global. Setelah semua kode dieksekusi, mesin mengambil konteks eksekusi global dari tumpukan saat ini.
Tentang membuat konteks dan menjalankan kode
Sejauh ini, kami berbicara tentang bagaimana mesin JS mengelola konteks eksekusi. Sekarang mari kita bicara tentang bagaimana konteks eksekusi dibuat, dan apa yang terjadi padanya setelah mereka dibuat. Secara khusus, kita berbicara tentang tahap menciptakan konteks eksekusi dan tahap eksekusi kode.
β Tahap menciptakan konteks eksekusi
Sebelum kode JavaScript dieksekusi, konteks eksekusi dibuat. Dalam proses penciptaannya, tiga tindakan dilakukan:
- Nilai ini ditentukan dan
this
(ini mengikat) terikat. - Komponen
LexicalEnvironment
dibuat. - Komponen
VariableEnvironment
dibuat.
Secara konseptual, konteks eksekusi dapat direpresentasikan sebagai berikut:
ExecutionContext = { ThisBinding = <this value>, LexicalEnvironment = { ... }, VariableEnvironment = { ... }, }
Ini mengikat
Dalam konteks eksekusi global,
this
berisi referensi ke objek global (seperti yang telah disebutkan, di browser itu adalah objek
window
).
Dalam konteks eksekusi fungsi, nilai
this
tergantung pada bagaimana fungsi dipanggil. Jika ini disebut sebagai metode objek, maka nilai
this
terikat ke objek ini. Dalam kasus lain,
this
terikat ke objek global atau diatur ke
undefined
(dalam mode ketat). Pertimbangkan sebuah contoh:
let foo = { baz: function() { console.log(this); } } foo.baz(); // 'this' 'foo', 'baz' // 'foo' let bar = foo.baz; bar(); // 'this' window, //
Lingkungan leksikal
Menurut
spesifikasi ES6, Lingkungan Leksikal adalah istilah yang digunakan untuk mendefinisikan hubungan antara pengidentifikasi dan variabel individu dan fungsi berdasarkan pada struktur lexical nesting dari kode skrip ECMAS. Lingkungan leksikal terdiri dari Catatan Lingkungan dan referensi ke lingkungan leksikal eksternal, yang dapat menjadi
null
.
Sederhananya, lingkungan leksikal adalah struktur yang menyimpan informasi tentang korespondensi pengidentifikasi dan variabel. Di sini, dengan "pengidentifikasi" berarti nama variabel atau fungsi, dan "variabel" adalah referensi ke objek tertentu (termasuk fungsi) atau nilai primitif.
Dalam lingkungan leksikal ada dua komponen:
- Catatan lingkungan. Di sinilah deklarasi variabel dan fungsi disimpan.
- Tautan ke lingkungan eksternal. Kehadiran tautan semacam itu menunjukkan bahwa lingkungan leksikal memiliki akses ke lingkungan leksikal induk (ruang lingkup).
Ada dua jenis lingkungan leksikal:
- Lingkungan global (atau konteks eksekusi global) adalah lingkungan leksikal yang tidak memiliki lingkungan eksternal. Referensi lingkungan global ke lingkungan eksternal adalah
null
. Di lingkungan global (dalam catatan lingkungan), entitas bahasa bawaan (seperti Object
, Array
, dan sebagainya) tersedia yang terkait dengan objek global, ada juga variabel global yang ditentukan oleh pengguna. Nilai this
dalam lingkungan ini menunjuk ke objek global. - Lingkungan fungsi di mana, dalam catatan lingkungan, variabel yang dideklarasikan oleh pengguna disimpan. Referensi ke lingkungan eksternal dapat menunjukkan objek global dan fungsi eksternal untuk fungsi yang dimaksud.
Ada dua jenis catatan lingkungan:
- Catatan lingkungan deklaratif yang menyimpan variabel, fungsi, dan parameter.
- Catatan objek lingkungan yang digunakan untuk menyimpan informasi tentang variabel dan fungsi dalam konteks global.
Akibatnya, dalam lingkungan global, rekaman lingkungan diwakili oleh catatan lingkungan objek, dan dalam lingkungan fungsi, oleh catatan lingkungan deklaratif.
Perhatikan bahwa dalam lingkungan fungsi, catatan deklaratif lingkungan juga berisi objek
arguments
, yang menyimpan korespondensi antara indeks dan nilai argumen yang diteruskan ke fungsi, dan informasi tentang jumlah argumen tersebut.
Lingkungan leksikal dapat direpresentasikan sebagai pseudocode berikut:
GlobalExectionContext = { LexicalEnvironment: { EnvironmentRecord: { Type: "Object", // } outer: <null> } } FunctionExectionContext = { LexicalEnvironment: { EnvironmentRecord: { Type: "Declarative", // } outer: < > } }
Variabel lingkungan
Lingkungan Variabel juga merupakan lingkungan leksikal yang rekaman lingkungannya menyimpan binding yang dibuat menggunakan perintah
VariableStatement
dalam konteks eksekusi saat ini.
Karena lingkungan variabel juga merupakan lingkungan leksikal, ia memiliki semua properti lingkungan leksikal yang dijelaskan di atas.
Dalam ES6, ada satu perbedaan antara komponen
LexicalEnvironment
dan
VariableEnvironment
. Terdiri dari fakta bahwa yang pertama digunakan untuk menyimpan deklarasi fungsi dan variabel yang dideklarasikan menggunakan kata kunci
let
dan
const
, dan yang terakhir hanya digunakan untuk menyimpan binding variabel yang dideklarasikan menggunakan kata kunci
var
.
Pertimbangkan contoh-contoh yang menggambarkan apa yang baru saja kita diskusikan:
let a = 20; const b = 30; var c; function multiply(e, f) { var g = 20; return e * f * g; } c = multiply(20, 30);
Representasi skematis dari konteks eksekusi untuk kode ini akan terlihat seperti ini:
GlobalExectionContext = { ThisBinding: <Global Object>, LexicalEnvironment: { EnvironmentRecord: { Type: "Object", // a: < uninitialized >, b: < uninitialized >, multiply: < func > } outer: <null> }, VariableEnvironment: { EnvironmentRecord: { Type: "Object", // c: undefined, } outer: <null> } } FunctionExectionContext = { ThisBinding: <Global Object>, LexicalEnvironment: { EnvironmentRecord: { Type: "Declarative", // Arguments: {0: 20, 1: 30, length: 2}, }, outer: <GlobalLexicalEnvironment> }, VariableEnvironment: { EnvironmentRecord: { Type: "Declarative", // g: undefined }, outer: <GlobalLexicalEnvironment> } }
Seperti yang mungkin Anda perhatikan, variabel dan konstanta yang dideklarasikan menggunakan kata kunci
let
dan
const
tidak memiliki nilai yang terkait, dan variabel yang dideklarasikan menggunakan kata kunci
var
diatur ke
undefined
.
Ini karena selama pembuatan konteks, kode mencari deklarasi variabel dan fungsi, sedangkan deklarasi fungsi disimpan sepenuhnya di lingkungan. Nilai-nilai variabel, ketika menggunakan
var
, diatur ke
undefined
, dan ketika menggunakan
let
atau
const
tetap tidak diinisialisasi.
Itulah sebabnya Anda dapat mengakses variabel yang dideklarasikan dengan
var
sebelum dideklarasikan (walaupun mereka
undefined
akan
undefined
), tetapi ketika Anda mencoba mengakses variabel atau konstanta yang dideklarasikan dengan
let
dan
const
dieksekusi sebelum mereka dideklarasikan, kesalahan .
Apa yang baru saja kami jelaskan disebut "variabel pengangkat". Deklarasi variabel βnaikβ ke atas lingkup leksikal mereka sebelum melakukan operasi untuk memberikan nilai apa pun kepada mereka.
β Tahap pelaksanaan kode
Ini mungkin bagian paling sederhana dari materi ini. Pada tahap ini, nilai diberikan ke variabel dan kode dieksekusi.
Harap dicatat bahwa jika, selama eksekusi kode, mesin JS tidak dapat menemukan nilai variabel yang dideklarasikan menggunakan kata kunci
let
di tempat deklarasi, itu akan memberikan variabel ini nilai yang
undefined
.
Ringkasan
Kami baru saja membahas mekanisme internal untuk mengeksekusi kode JavaScript. Meskipun untuk menjadi pengembang JS yang sangat baik, tidak perlu mengetahui semua ini, jika Anda memiliki pemahaman tentang konsep-konsep di atas, itu akan membantu Anda lebih baik dan lebih dalam memahami mekanisme bahasa lainnya, seperti meningkatkan variabel, ruang lingkup, sirkuit pendek.
Pembaca yang budiman! Apa lagi yang menurut Anda selain konteks eksekusi dan tumpukan panggilan yang berguna untuk diketahui oleh pengembang JavaScript?
