JavaScript မှာ Code တွေကို internally ဘယ်လိုအလုပ်လုပ်ပြီး execute လုပ်လဲဆိုတဲ့အကြောင်းဖြစ်ပါတယ်။
Intro
JavaScript ဟာ သုံးရတာ တော်တော်လေးလွယ်ကူတဲ့ programming language တစ်ခုဖြစ်ပေမယ့်လည်း တစ်ချိန်တည်းမှာ weiredest language တစ်ခုလည်းဖြစ်ပါတယ်။ သူ့ရဲ့ internal fundamental concept တွေကို နားလည်မထားတာဟာ တစ်ချိန်ချိန်မှာတော့ ကောင်းကောင်း ဒုက္ခပေးတတ်ပါတယ်။
ဒီ Note မှာကတော့ fundamental concept တစ်ခုဖြစ်တဲ့ “Execution Context” အကြောင်းကိုပြောပြသွားမှာဖြစ်ပါတယ်။ Execution Context ကို နားလည်ခြင်းအားဖြင့် အခြားသော concept တွေဖြစ်တဲ့ Hoisting, Scope Chains, Closure, this စတာတွေကို ဆက်လက်လေ့လာသွားနိုင်မှာဖြစ်ပါတယ်။ ဆွေးနွေးသွားမှာတွေကတော့ -
- What is Execution Context
- Types of Execution Context
- Phases
- Call Stack
- Common Problem: Stack Overflow
What is Execution Context
ပထမဆုံးအနေနဲ့ Execution Context ဆိုတဲ့ buzz word ကြီးကို break down လုပ်ကြရအောင်။ Execution ဆိုတာ အားလုံးသိတဲ့အတိုင်း code တစ်ခုခုကို compiler (သို့) interpreter က run တာကိုဆိုလိုတာဖြစ်ပါတယ်။ Context ဆိုတာကို အခြေအနေတစ်ရပ် လို့ပဲ မှတ်ယူကြရအောင်။
အားလုံးသိတဲ့အတိုင်း ရှုပ်ထွေးပြီးကြီးမားတဲ့ကိစ္စတွေကို ဘယ်လိုနည်းလမ်းနဲ့ ကိုင်တွယ်ဖြေရှင်းလေ့ရှိလဲဆိုတော့ သေးငယ်တဲ့ task တွေအနေနဲ့ခွဲခြမ်းစိတ်ဖြာပြီး ဖြေရှင်းလေ့ရှိပါတယ်။ ကျနော်တို့ code တွေရေးတဲ့ အခါမှာလည်း ဒီလိုပါပဲ။ Feature တစ်ခုကို implement လုပ်တဲ့အခါမှာ function တွေ module တွေ class တွေ package တွေ အစရှိသဖြင့် ခွဲပြီးရေးသလိုမျိုးပါပဲ။ JavaScript Engine ကလည်း code တွေကို space (နေရာ) တွေခွဲပြီး execute လုပ်ပါတယ်။
ကျနော်တို့တတွေ “ငါဘယ် file ကို run ရမှာပါလိမ့် ?” ဆိုပြီးတွေးလေ့ရှိသလိုပဲ “Execution Context” ဟာ တကယ်တော့ JavaScript Engine က “ငါ ဘယ် code ကို run နေတာပါလိမ့် ?” ဆိုတာကို အဖြေရှာပေးဖို့ဖြစ်ပါတယ်။
Types of Execution Context
Execution Context ၃ မျိုးရှိပါတယ် -
- Global Execution Context
- Function Execution Context
- Eval Function Execution Context
Global Execution Context
Global Execution Context ဟာ JavaScript Code တိုင်းရဲ့ default execution context ဖြစ်ပါတယ်။ ဘာ code မှ မရေးရသေးတဲ့အချိန်မှာတောင် Global context ရှိနေမှာဖြစ်ပါတယ်။ ဘယ် function နဲ့မှ မသက်ဆိုင်တဲ့ code တွေတိုင်းဟာ Global Execution Context ထဲမှာရှိနေမှာဖြစ်ပါတယ်။ Global Execution Context မှာ ပါဝင်တာတွေကတော့
- global object တစ်ခုရယ်
- this ဆိုတဲ့ variable တစ်ခုရယ် ဖြစ်ပါတယ်။
Global Object ဟာ Browser environment မှာ window ဖြစ်ပြီးတော့ Node.js မှာတော့ global ဆိုတဲ့ object ဖြစ်ပါတယ်။ ဒီမှာ this က ဒီ global object ရဲ့ reference ဖြစ်ပါတယ်။
Global in Node.js
Global in Browser
Function Execution Context
Function Context ကတော့ function တစ်ခု invoke လုပ်တဲ့အခါတိုင်းမှာ JavaScript က Context တစ်ခုအနေနဲ့ create လုပ်သွားတာဖြစ်ပါတယ်။ ဒီတော့ function invoke လုပ်တဲ့အခါတိုင်းမှာ Execution Context အသစ်တစ်ခု တည်ဆောက်တယ် ဆိုလို့ရှိရင် ဒီ Context တွေကို အစဥ်လိုက် execute လုပ်ဖို့အတွက်တစ်ခုခုရှိဖို့ လိုအပ်လာပါတယ်။ ဒါကို Call Stack ကတာဝန်ယူပါတယ်။
Phases
Execution Context ကို create လုပ်ရတဲ့ Creation Phase နဲ့ တကယ် Execute လုပ်တဲ့ Execution Phase ဆိုပြီး phase ၂ခု ရှိပါတယ်။
1. Creation Phase
ဒီ phase မှာ JavaScript ဟာ Execution Context တစ်ခုကိုတည်ဆောက်ဖို့အတွက် prepare လုပ်တာဖြစ်ပါတယ်။
Steps in Global Execution Context Creation Phase
- Global Object ကိုတည်ဆောက်
- this ဆိုတဲ့ Object ကို create လုပ်
- variable တွေ function တွေအတွက် memory ပေါ်မှာနေရာယူ
- hoisting လုပ်
JavaScript မှာ hoisting ဆိုတာကတော့ တိုတိုရှင်းရှင်းပြောရရင် Creation Phase မှာ declared ထားတဲ့ Variable တွေရဲ့ value ကို undefined ဆိုပြီး default အနေနဲ့ assign လုပ်ပေးတဲ့ process တစ်ခုဖြစ်ပါတယ်။
Steps in Function Execution Context Creation Phase
Global Context နဲ့ ကွာတာကတော့ function တစ်ခုမှာ Arguments တွေရှိတတ်တာဖြစ်ပါတယ်။ ဒါ့ကြောင့်
- Arguments object တစ်ခုကိုတည်ဆောက်
- this ကို create လုပ်
- variable, function တွေအတွက် memory space ယူ
- hoisting လုပ်
Creation Phase
2. Execution Phase
ဒီ phase မှာတော့ line by line interpret လုပ်ပြီး execute လုပ်တာဖြစ်ပါတယ်။ Execution Phase ကို သေချာနားလည်ဖို့ Call Stack အကြောင်းရှင်းပြဖို့လိုလာမှာဖြစ်တဲ့အတွက် Call Stack ဘက် အရင်လှည့်လိုက်ရအောင်။
Call Stack
အရင်ဆုံးအနေနဲ့ Stack လို့ခေါ်တဲ့ Data Structure အကြောင်းကို နည်းနည်းနားလည်ထားဖို့လိုပါမယ်။ (Stack Data Structure အကြောင်းကို ဒီ Article မှာဆွေးနွေးခဲ့ပြီးဖြစ်ပါတယ်။) Stack ရဲ့ nature ဟာ First-in-Last-out (FILO) တစ်နည်း Last-in-First-out (LIFO) ဖြစ်ပါတယ်။ ဆိုလိုတာကတော့ ပထမဆုံးဝင်လာတဲ့ data item တစ်ခုဟာ နောက်ဆုံးမှသာပြန်ထွက်လို့ရတာဖြစ်ပါတယ်။ တစ်နည်းအားဖြင့် နောက်ဆုံးဝင်တဲ့ data item ဟာ ပထမဆုံးထွက်ခွင့်ရတာဖြစ်ပါတယ်။
အပေါ်မှာတုန်းက ပြောခဲ့သလိုပါပဲ၊ Context တွေကို အစဥ်လိုက် execute လုပ်ဖို့အတွက် Call Stack ကစီမံပါတယ်။ အောက်က code ကို တစ်ချက်ကြည့်လိုက်ရအောင်
function last() {
console.log('Last Func Called')
}
function third() {
last()
console.log('Third Func Called')
}
function second() {
third()
console.log('Second Func Called')
}
function first() {
second()
console.log('First Func Called')
}
first()
ဒီမှာ first -> second -> third -> last ဆိုပြီးတော့ အစဥ်လိုက် function ခေါ်ထားတာကိုတွေ့ရမှာဖြစ်ပြီးတော့ output အနေနဲ့ကတော့ ဒီလိုထွက်ပါတယ်။ (Function invoke ပြီးမှ console log ထားတာကို သတိချပ်သင့်ပါတယ်)
Last Func Called
Third Func Called
Second Func Called
First Func Called
ယခုလို invocation ကို Call Stack ပေါ်မှာ ဘယ်လိုမြင်နိုင်မလဲဆိုတော့
Invocation ရဲ့ အစဥ်လိုက်အနေနဲ့က first -> second -> third -> last ဆိုပြီး call ခဲ့ပေမယ့် JavaScript က execute လုပ်တဲ့အခါမှာတော့ First-in-Last-out ဖြစ်တဲ့အတွက် last -> third -> second -> first ဆိုပြီးအစဥ်လိုက် execute လုပ်ပါတယ်။ ဒါ့ကြောင့် Stack ရဲ့အပေါ်ဆုံးကစပြီး execute လုပ်တယ်။ Complete ဖြစ်သွားရင် Stack ပေါ်က pop (ထုတ်) လိုက်မယ်။ ပြီးရင်အောက်က ဆက်ရှိတဲ့ function တွေကို ဆက်ပြီး execute လုပ်ပါတယ်။ ဒီ process ကို Call Stack empty ဖြစ်သွားတဲ့အထိ ဆက်လုပ်နေမှာဖြစ်ပါတယ်။ Call Stack Empty ဖြစ်သွားပြီဆိုရင်တော့ ဒါဟာ program ပြီးသွားပြီပဲဖြစ်ပါတယ်။
Common Problem: Stack Overflow
Stack Overflow ဆိုတာကို ခဏခဏ ကြားဖူးကြမယ်ထင်ပါတယ်။ ဒီ error ကတော့ဖြစ်လေ့ဖြစ်ထရှိတဲ့ error တစ်ခုဖြစ်ပြီးတော့ ဘယ်အချိန်မှာဖြစ်လေ့ရှိလဲဆိုတော့ Function invocation တွေဟာ အရမ်းများလွန်းတဲ့အတွက် Call Stack ပေါ်မှာ မသိမ်းနိုင်တော့တဲ့အချိန်မှာ ကြုံရတာဖြစ်ပါတယ်။ ဥပမာ -
function fire() {
fire()
}
fire()
Uncaught RangeError: Maximum call stack size exceeded
at WriteStream.getColorDepth (internal/tty.js:173:20)
at Object.Console.<computed> (internal/console/constructor.js:265:16)
at Object.Console.<computed> (internal/console/constructor.js:280:40)
at Object.log (internal/console/constructor.js:291:61)
at fire (repl:2:9)
at fire (repl:3:1)
at fire (repl:3:1)
at fire (repl:3:1)
at fire (repl:3:1)
at fire (repl:3:1)
Call Stack မှာဖြစ်ပျက်သွားတာကတော့
fire -> fire -> fire -> fire -> fire -> ….
Conclusion
- Execution Context ဆိုတာ JavaScript Engine က သူကိုယ်တိုင်ဘယ် code ကို execute လုပ်နေတာလဲဆိုတာသိဖို့အတွက်ဖြစ်ပါတယ်။
- Execution Context ကို နားလည်ခြင်းဟာ Closure, this စတဲ့ development တလျောက်တွေ့ကြုံရတာတွေအတွက်အခြေခံလိုအပ်ချက်ဖြစ်ပါတယ်။
- this ဟာ current execution context ကို ရည်ညွှန်းပါတယ်။
- Execution Context မှာ Creation နဲ့ Execution ဆိုပြီး phase ၂ခုရှိပါတယ်။
- Call Stack ထဲမှာ execute ရမယ့် Code အစဥ်လိုက်ရှိနေမှာဖြစ်ပြီး empty ဖြစ်တဲ့အထိ ဆက်လုပ်နေမှာဖြစ်ပါတယ်။
- Call Stack Size ထက် ကျော်လွန်တဲ့ execution တွေဟာ Stack Overflow error ကိုဖြစ်စေပါတယ်။