મોટાભાગના લોકો JavaScript ની શરૂઆત વસ્તુઓ બનાવીને કરે છે. તમે એક બટન સાથે કનેક્શન કરો છો, થોડો ડેટા ફેચ કરો છો અને DOM માં થતા ફેરફારો જુઓ છો. પછી એબ્સ્ટ્રેક્શન લીક (abstractions leak) થાય છે. એવા બગ્સ સામે આવે છે જેનો કોઈ અર્થ નીકળતો નથી: વેરિયેબલ્સ તેમની ડિક્લેરેશન પહેલાં અસ્તિત્વ ધરાવે છે, ફંક્શન્સ એવા વેરિયેબલ્સ યાદ રાખે છે જે સુધી તેઓ પાસે એક્સેસ હોવો જોઈએ નહીં, અને this કીવર્ડ વિન્ડો (window), બટન અથવા કંઈ જ નહીં તરફ નિર્દેશ કરે છે. સામાન્ય રીતે આ એ ક્ષણ હોય છે જ્યારે તમને સમજાય છે કે એન્જિન ખરેખર શું કરી રહ્યું છે તે સમજવા માટે તમારે તેની અંદરની કાર્યપદ્ધતિ જોવી જરૂરી છે.

એક્ઝિક્યુશન કોન્ટેક્સ્ટ (Execution Context): બે તબક્કાનું સેટઅપ

જ્યારે તમારા બ્રાઉઝર અથવા Node.js માં JavaScript એન્જિન કોઈ સ્ક્રિપ્ટનો સામનો કરે છે, ત્યારે તે કોઈ વ્યક્તિ પેજ વાંચતી હોય તેમ ફાઇલને ઉપરથી નીચે સુધી ફક્ત વાંચી લેતું નથી. તેના બદલે, તે એક્ઝિક્યુશન કોન્ટેક્સ્ટ (execution context) બનાવે છે, જે એક કન્ટેનર છે જે કોડના ચોક્કસ ભાગને ચલાવવા માટે જરૂરી બધું જ ધરાવે છે. દરેક એક્ઝિક્યુશન કોન્ટેક્સ્ટ બે અલગ-અલગ તબક્કાઓમાંથી પસાર થાય છે.

મેમરી ક્રિએશન ફેઝ (Memory Creation Phase). આ પ્રારંભિક તબક્કા દરમિયાન, એન્જિન સમગ્ર સ્કોપને સ્કેન કરે છે અને તેને મળતા દરેક વેરિયેબલ અને ફંક્શન ડિક્લેરેશન માટે મેમરી ફાળવે છે. જો તેને var દેખાય, તો તે જગ્યા અનામત રાખે છે અને પ્લેસહોલ્ડર તરીકે undefined સ્ટોર કરે છે. જો તેને ફંક્શન ડિક્લેરેશન દેખાય, તો તે સંપૂર્ણ ફંક્શન બોડી સ્ટોર કરે છે. આ જ કારણ છે કે પરંપરાગત function કીવર્ડ સાથે જાહેર કરાયેલ ફંક્શનને તે જ સ્કોપમાં અગાઉની લાઇનોથી પણ કોલ કરી શકાય છે. એન્જિન તેના એક્ઝિક્યુશન શરૂ કરતા પહેલા જ તેના વિશે જાણી લે છે.

કોડ એક્ઝિક્યુશન ફેઝ (Code Execution Phase). હવે એન્જિન તમારી કોડને લાઇન બાય લાઇન રન કરે છે. અસાઇનમેન્ટ્સ અહીં થાય છે. એક્સપ્રેશન્સનું મૂલ્યાંકન કરવામાં આવે છે. ફંક્શન્સ ઇનવોક (invoke) કરવામાં આવે છે. જો તમે var name = "Alice"; લખ્યું હોય, તો પ્રથમ તબક્કાનું પ્લેસહોલ્ડર આખરે સ્ટ્રિંગ સાથે બદલાઈ જાય છે. આ બે-પાસ (two-pass) વર્તણૂક સમજવાથી અસંખ્ય મૂંઝવણો દૂર થાય છે. એન્જિન અસ્તવ્યસ્ત નથી; તે એક સખત સેટઅપ રૂટિનનું પાલન કરી રહ્યું છે.

વેરિયેબલ્સ અને ટેમ્પોરલ ડેડ ઝોન (Temporal Dead Zone)

var, let, અને const વચ્ચે પસંદગી કરવી એ માત્ર શૈલીગત પસંદગી નથી. var ફંક્શન-સ્કોપ્ડ (function-scoped) છે, જેનો અર્થ છે કે તે બ્રેસીસ (braces) ને સંપૂર્ણપણે અવગણે છે. તેને if બ્લોકની અંદર જાહેર કરો, અને તે બહાર નીકળી જાય છે. આ વર્તણૂક શરૂઆતના JavaScript માં તર્કસંગત હોઈ શકે છે, પરંતુ આધુનિક એપ્લિકેશન્સમાં તે મેન્ટેનન્સની મોટી સમસ્યાઓ ઊભી કરે છે. let અને const બ્લોક-સ્કોપ્ડ (block-scoped) છે. તેઓ કરલી બ્રેસીસ (curly braces) નું સન્માન કરે છે અને બ્લોક સમાપ્ત થાય ત્યારે અદૃશ્ય થઈ જાય છે.

હોઇસ્ટિંગ (hoisting) તેને કેવી રીતે ટ્રીટ કરે છે તેમાં પણ એક સૂક્ષ્મ પણ મહત્વપૂર્ણ તફાવત છે. var ડિક્લેરેશન હોઇસ્ટ થાય છે અને તરત જ undefined સાથે ઇનિશિયલાઇઝ થાય છે. let અને const ટેકનિકલી હોઇસ્ટ પણ થાય છે; એન્જિન ડિક્લેરેશન લાઇન સુધી પહોંચતા પહેલા જ જાણે છે કે તેઓ અસ્તિત્વ ધરાવે છે. પરંતુ તેઓ ઇનિશિયલાઇઝ થતા નથી. તેઓ ટેમ્પોરલ ડેડ ઝોન (temporal dead zone) તરીકે ઓળખાતા લિમ્બો (limbo) માં રહે છે. જો તમે ડિક્લેરેશન લાઇન એક્ઝિક્યુટ થાય તે પહેલાં તેમને વાંચવાનો પ્રયાસ કરો છો, તો તમને છૂપી undefined ને બદલે સખત ReferenceError મળશે. આ ક્રેશ ખરેખર મદદરૂપ છે. તે અનિશ્ચિત ડેટાના આધારે લોજિક આગળ વધતું અટકાવે છે.

લેક્સિકલ સ્કોપ (Lexical Scope) અને ક્લોઝર્સ (Closures)

સ્કોપ એક સરળ પ્રશ્નનો જવાબ આપે છે: હું આ વેરિયેબલને ક્યાં એક્સેસ કરી શકું છું? JavaScript લેક્સિકલ સ્કોપ (lexical scope) નો ઉપયોગ કરે છે, જેનો અર્થ છે કે ફંક્શનના એક્સેસ અધિકારો તે સોર્સ કોડમાં ક્યાં લખવામાં આવ્યું છે તેના દ્વારા નક્કી કરવામાં આવે છે, નહીં કે તેને ક્યાં કોલ કરવામાં આવે છે તેના દ્વારા. જો તમે એક ફંક્શનની અંદર બીજું ફંક્શન વ્યાખ્યાયિત કરો છો, તો અંદરનું ફંક્શન તેના પેરેન્ટમાંથી વેરિયેબલ્સ વાંચવા માટે બહાર સુધી પહોંચી શકે છે. બહારનું ફંક્શન અંદર સુધી પહોંચી શકતું નથી. આ સંબંધ સ્થિર (static) છે. તમે તે અંદરના ફંક્શનને મોડ્યુલ્સમાં પાસ કરી શકો છો, તેને ગ્લોબલ વેરિયેબલમાં સ્ટોર કરી શકો છો, અને તેને તદ્દન અલગ ફાઇલમાંથી કોલ કરી શકો છો. તે હજુ પણ તે સ્કોપના વેરિયેબલ્સ યાદ રાખે છે જ્યાં તેનો જન્મ થયો હતો.

આ વર્તણૂક કુદરતી રીતે ક્લોઝર્સ (closures) પેદા કરે છે. ક્લોઝર ત્યારે બને છે જ્યારે અંદરનું ફંક્શન તેના બહારના સ્કોપના વેરિયેબલનો સંદર્ભ (reference) રાખે છે. બહારનું ફંક્શન એક્ઝિક્યુટ કરવાનું પૂરું કર્યા પછી અને તેના લોકલ વેરિયેબલ્સ ગાર્બેજ કલેક્ટ (garbage collected) થઈ જવા જોઈએ તે પછી પણ, JavaScript તેમને મેમરીમાં સાચવે છે કારણ કે અંદરના ફંક્શનને હજુ પણ તેમની જરૂર હોય છે. અંદરનું ફંક્શન તેની આસપાસના વાતાવરણને પોતાની સાથે રાખે છે.

આ માત્ર શૈક્ષણિક વિગત નથી. ક્લોઝર્સ તમને એવી ભાષામાં પ્રાઇવેટ સ્ટેટ (private state) બનાવવાની વ્યવહારુ રીત આપે છે જેમાં સ્પષ્ટ એક્સેસ મોડિફાયર્સનો અભાવ છે.

function makeCounter() {
  let count = 0;
  return function() {
    count = count + 1;
    return count;
  };
}

const counter = makeCounter();
console.log(counter()); // 1
console.log(counter()); // 2

અહીં, count છુપાયેલું છે. રિટર્ન કરેલા ફંક્શનની બહાર કંઈપણ તેને રીસેટ કરી શકતું નથી અથવા સીધું વાંચી શકતું નથી. તે માત્ર સ્કોપ મિકેનિક્સ દ્વારા બનાવવામાં આવેલ એક પ્રાઇવેટ વેરિયેબલ છે.

પ્રેક્ટિસમાં હોઇસ્ટિંગ (Hoisting in Practice)

એવું સાંભળવું સામાન્ય છે કે JavaScript "ડિક્લેરેશનને ટોપ પર લઈ જાય છે." તે એક ઉપયોગી માનસિક મોડેલ છે, પરંતુ કોડ ખરેખર ફરીથી લખવામાં આવતો નથી. મેમરી ક્રિએશન ફેઝ (memory creation phase) દરમિયાન, એન્જિન એક્ઝિક્યુશન શરૂ થાય તે પહેલાં ફક્ત ડિક્લેરેશન રજિસ્ટર કરે છે. var x = 5; જેવું સ્ટેટમેન્ટ એવું વર્તન કરે છે જાણે કે ડિક્લેરેશન અને ઇનિશિયલાઇઝેશન અલગ-અલગ હોય. var x; ડિક્લેરેશન વહેલું પ્રોસેસ થાય છે અને undefined તરીકે ઇનિશિયલાઇઝ થાય છે. એસાઇનમેન્ટ x = 5; તમે જ્યાં લખ્યું છે ત્યાં જ રહે છે અને એક્ઝિક્યુશન ફેઝ દરમિયાન ચાલે છે.

આ કારણે, var આશ્ચર્યજનક પરિણામો લાવી શકે છે. ફંક્શનના ટોપ પાસે વપરાયેલ વેરિએબલ undefined હોઈ શકે છે, ભલે નીચેના ભાગમાં મોટું એસાઇનમેન્ટ હોય. let અને const નો ઉપયોગ કરવાથી આ સમસ્યા દૂર થાય છે કારણ કે 'ટેમ્પોરલ ડેડ ઝોન' (temporal dead zone) તમને તમારા ડિક્લેરેશનનો ઉપયોગ કરતા પહેલા તેને લખવા માટે મજબૂર કરે છે.

this ને તેની વેલ્યુ કેવી રીતે મળે છે

જો એક્ઝિક્યુશન કોન્ટેક્સ્ટ (execution context) અને સ્કોપ (scope) નક્કી કરે કે વેરિએબલ્સ ક્યાં રહે છે, તો this નક્કી કરે છે કે હાલમાં કયો ઓબ્જેક્ટ ઇન-ચાર્જ છે. લેક્સિકલ વેરિએબલ્સથી વિપરીત, this ફંક્શન ક્યાં લખવામાં આવ્યું છે તેનાથી નક્કી થતું નથી. તે સંપૂર્ણપણે ફંક્શન કેવી રીતે કોલ કરવામાં આવ્યું છે તેના દ્વારા નક્કી થાય છે.

Default binding ત્યારે થાય છે જ્યારે તમે એક સાદું, સ્ટેન્ડઅલોન ફંક્શન ઇનવોક કરો છો. નોન-સ્ટ્રિક્ટ મોડમાં, this ગ્લોબલ ઓબ્જેક્ટ પર જાય છે. બ્રાઉઝરમાં, તે window છે. કોઈપણ કોન્ટેક્સ્ટ વગર ફંક્શન કોલ કરો, અને તમે અજાણતા ગ્લોબલ સ્ટેટને સ્પર્શી શકો છો.

Implicit binding ત્યારે થાય છે જ્યારે તમે કોઈ ઓબ્જેક્ટ પર મેથડ તરીકે ફંક્શન કોલ કરો છો. જો તમે user.sayName() લખો છો, તો ડોટ (dot) એન્જિનને તે કોલ દરમિયાન this ને user તરીકે સેટ કરવા માટે શાંતિથી કહે છે. ફંક્શન ક્યાં વ્યાખ્યાયિત (define) કરવામાં આવ્યું છે તેના કરતા તે ક્યાં કોલ કરવામાં આવ્યું છે તે વધુ મહત્વનું છે.

Explicit binding તમને બધું મેન્યુઅલી ઓવરરાઈડ કરવાની મંજૂરી આપે છે. call() અને apply() ફંક્શનને તરત જ ઇનવોક કરે છે અને સાથે જ this ને તમે આપેલા ચોક્કસ ઓબ્જેક્ટ તરીકે સેટ કરે છે. તેમની વચ્ચેનો એકમાત્ર તફાવત એ છે કે આર્ગ્યુમેન્ટ્સ કેવી રીતે પાસ કરવામાં આવે છે: call કોમા-સેપરેટેડ લિસ્ટ લે છે, જ્યારે apply એરે (array) લે છે. bind() અલગ રીતે કામ કરે છે. તે તરત જ ફંક્શનને ઇનવોક કરતું નથી. તેના બદલે, તે એક નવું ફંક્શન રિટર્ન કરે છે જેમાં this કાયમી ધોરણે તમે આપેલ વેલ્યુ સાથે લોક હોય છે. આ કોલબેક્સ (callbacks) માટે અમૂલ્ય છે જે કદાચ અન્ય જગ્યાએ પાસ કરતી વખતે તેમનો કોન્ટેક્સ્ટ ગુમાવી શકે છે.

New binding ત્યારે કામમાં આવે છે જ્યારે તમે ફંક્શન કોલની આગળ new કીવર્ડનો ઉપયોગ કરો છો. એન્જિન એક નવો ખાલી ઓબ્જેક્ટ બનાવે છે, તેનું પ્રોટોટાઇપ લિંકેજ સેટ કરે છે, અને કન્સ્ટ્રક્ટરની અંદર this ને તે નવા ઇન્સ્ટન્સ તરફ નિર્દેશિત કરે છે.

લાંબા સમય સુધી ટકી રહે તેવો કોડ લખવો

મિકેનિક્સ સમજવું એ કળાનો માત્ર અડધો ભાગ છે. બીજો અડધો ભાગ એ છે કે એવો કોડ લખવો જે માણસો છ મહિના પછી પણ વાંચી શકે.

DRY (Don't Repeat Yourself) સાંભળવામાં સ્પષ્ટ લાગે છે, પરંતુ તેનું સતત ઉલ્લંઘન થાય છે. જો તમે તમારી જાતને ત્રણ અલગ-અલગ ફાઇલોમાં સમાન વેલિડેશન લોજિક અથવા API કોલ પેટર્ન લખતા જુઓ છો, તો તેને અલગ તારવો (extract). એક જ ફંક્શન લખો. 'વન સોર્સ ઓફ ટ્રુથ' (one source of truth) એટલે જ્યારે જરૂરિયાતો બદલાય ત્યારે અપડેટ કરવા માટે એક જ જગ્યા, અને તે સમય બચાવે છે જેનું મહત્વ વર્ણવવું મુશ્કેલ છે.

KISS (Keep It Simple, Stupid) એ અહંકાર સામેનો બચાવ છે. નેસ્ટેડ ટેરનરીઝ (Nested ternaries) અને વન-લાઇનર ક્લોઝર્સ (one-liner closures) હોશિયાર લાગે છે, પરંતુ ડિબગિંગ દરમિયાન તે કલાકોનો સમય લે છે. મેં અગાઉ બતાવેલ ક્લોઝર પેટર્ન શક્તિશાળી છે, છતાં માત્ર તમે કરી શકો છો એટલે પાંચ લેવલ ઊંડું નેસ્ટિંગ કરવું એ ભૂલ છે. સાદો કોડ ટીમ બદલાવા, પ્રોડક્શનની ઘટનાઓ અને મધ્યરાત્રિના એ એલર્ટ્સમાં પણ ટકી રહે છે જ્યારે કંઈક તૂટી જાય છે અને કોઈને યાદ નથી હોતું કે કેમ.

સાચું વળતર

એક્ઝિક્યુશનનો અભ્યાસ કરવો