മിക്ക ആളുകളും ജാവാസ്ക്രിപ്റ്റ് പഠിച്ചു തുടങ്ങുന്നത് കാര്യങ്ങൾ നിർമ്മിച്ചുകൊണ്ടാണ്. നിങ്ങൾ ഒരു ബട്ടൺ കണക്ട് ചെയ്യുന്നു, കുറച്ച് ഡാറ്റ ഫെച്ച് ചെയ്യുന്നു, ഡൊം (DOM) മാറുന്നത് നോക്കിനിൽക്കുന്നു. എന്നാൽ പിന്നീട് അബ്സ്ട്രാക്ഷനുകൾ പാളുന്നു (abstractions leak). യുക്തിക്ക് നിരക്കാത്ത ബഗ്ഗുകൾ പ്രത്യക്ഷപ്പെടുന്നു: വേരിയബിളുകൾ അവ പ്രഖ്യാപിക്കുന്നതിന് മുമ്പ് തന്നെ നിലനിൽക്കുന്നു, ഫങ്ക്ഷനുകൾ അവർക്ക് പ്രവേശനമില്ലാത്ത വേരിയബിളുകളെ ഓർമ്മിച്ചുവെക്കുന്നു, കൂടാതെ this കീവേഡ് വിൻഡോയെയായോ, ഒരു ബട്ടണിനെയോ, അല്ലെങ്കിൽ ഒന്നുമില്ലാത്ത അവസ്ഥയെയോ ആണ് ചൂണ്ടിക്കാണിക്കുന്നത്. എൻജിൻ യഥാർത്ഥത്തിൽ എന്താണ് ചെയ്യുന്നതെന്ന് മനസ്സിലാക്കാൻ അതിന്റെ ഉള്ളിലെ പ്രവർത്തനം പരിശോധിക്കേണ്ട സമയമാണിതെന്ന് നിങ്ങൾ തിരിച്ചറിയുന്ന നിമിഷമാണത്.

എക്സിക്യൂഷൻ കോൺടെക്സ്റ്റ്: രണ്ട് ഘട്ടങ്ങളുള്ള സജ്ജീകരണം (Execution Context: The Two-Phase Setup)

നിങ്ങളുടെ ബ്രൗസറിലെ അല്ലെങ്കിൽ Node.js-ലെ ജാവാസ്ക്രിപ്റ്റ് എൻജിൻ ഒരു സ്ക്രിപ്റ്റ് കാണുമ്പോൾ, ഒരു പേജ് വായിക്കുന്നത് പോലെ മുകളിൽ നിന്ന് താഴേക്ക് വെറുതെ വായിക്കുകയല്ല ചെയ്യുന്നത്. പകരം, അത് ഒരു 'എക്സിക്യൂഷൻ കോൺടെക്സ്റ്റ്' (execution context) നിർമ്മിക്കുന്നു; ഒരു പ്രത്യേക കോഡ് ഭാഗം പ്രവർത്തിപ്പിക്കാൻ ആവശ്യമായതെല്ലാം ഉൾക്കൊള്ളുന്ന ഒരു കണ്ടെയ്നറാണിത്. ഓരോ എക്സിക്യൂഷൻ കോൺടെക്സ്റ്റും രണ്ട് വ്യത്യസ്ത ഘട്ടങ്ങളിലൂടെ കടന്നുപോകുന്നു.

മെമ്മറി ക്രിയേഷൻ ഫേസ് (Memory Creation Phase). ഈ ആദ്യ ഘട്ടത്തിൽ, എൻജിൻ മുഴുവൻ സ്കോപ്പും പരിശോധിക്കുകയും കണ്ടെത്തുന്ന ഓരോ വേരിയബിളിനും ഫങ്ക്ഷൻ ഡിക്ലറേഷനും വേണ്ടി മെമ്മറി അനുവദിക്കുകയും ചെയ്യുന്നു. ഒരു var കണ്ടാൽ, അത് സ്ഥലം റിസർവ് ചെയ്യുകയും undefined ഒരു പ്ലേസ്‌ഹോൾഡറായി സൂക്ഷിക്കുകയും ചെയ്യുന്നു. ഒരു ഫങ്ക്ഷൻ ഡിക്ലറേഷൻ കണ്ടാൽ, അത് പൂർണ്ണമായ ഫങ്ക്ഷൻ ബോഡി തന്നെ സൂക്ഷിക്കുന്നു. അതുകൊണ്ടാണ് പരമ്പരാഗത function കീവേഡ് ഉപയോഗിച്ച് പ്രഖ്യാപിച്ച ഒരു ഫങ്ക്ഷൻ, അതേ സ്കോപ്പിലെ മുൻപുള്ള വരികളിൽ നിന്ന് തന്നെ വിളിക്കാൻ കഴിയുന്നത്. എൻജിൻ അത് പ്രവർത്തിപ്പിക്കാൻ തുടങ്ങുന്നതിന് മുമ്പ് തന്നെ അതിനെക്കുറിച്ച് അറിയുന്നുണ്ട്.

കോഡ് എക്സിക്യൂഷൻ ഫേസ് (Code Execution Phase). ഇപ്പോൾ എൻജിൻ നിങ്ങളുടെ കോഡ് ഓരോ വരിയായി പ്രവർത്തിപ്പിക്കുന്നു. അസൈൻമെന്റുകൾ ഇവിടെ നടക്കുന്നു. എക്സ്പ്രഷനുകൾ വിലയിരുത്തപ്പെടുന്നു. ഫങ്ക്ഷനുകൾ ഇൻവോക്ക് ചെയ്യപ്പെടുന്നു. നിങ്ങൾ var name = "Alice"; എന്ന് എഴുതിയിട്ടുണ്ടെങ്കിൽ, ഒന്നാം ഘട്ടത്തിലെ പ്ലേസ്‌ഹോൾഡറിന് പകരം ആ സ്ട്രിംഗ് വരുന്നു. ഈ രണ്ട് ഘട്ടങ്ങളുള്ള പ്രവർത്തനം മനസ്സിലാക്കുന്നത് വലിയൊരു സംശയവും പരിഹരിക്കാൻ സഹായിക്കും. എൻജിൻ അശ്രദ്ധമായ ഒന്നല്ല; അത് കൃത്യമായ ഒരു സജ്ജീകരണ രീതിയാണ് പിന്തുടരുന്നത്.

വേരിയബിളുകളും ടെമ്പറൽ ഡെഡ് സോണും (Variables and the Temporal Dead Zone)

var, let, const എന്നിവയിൽ നിന്ന് ഒരെണ്ണം തിരഞ്ഞെടുക്കുന്നത് വെറുമൊരു ശൈലി മാത്രമല്ല. var ഫങ്ക്ഷൻ സ്കോപ്പ് (function-scoped) ഉള്ളതാണ്, അതായത് ഇത് ബ്രേസുകളെ (braces) പൂർണ്ണമായും അവഗണിക്കുന്നു. ഒരു if ബ്ലോക്കിനുള്ളിൽ ഇത് പ്രഖ്യാപിച്ചാൽ, അത് പുറത്തേക്ക് വ്യാപിക്കും. പഴയ ജാവാസ്ക്രിപ്റ്റിൽ ഈ രീതിക്ക് അർത്ഥമുണ്ടാകാം, എന്നാൽ ആധുനിക ആപ്ലിക്കേഷനുകളിൽ ഇത് പരിപാലനത്തിൽ വലിയ ബുദ്ധിമുട്ടുകൾ ഉണ്ടാക്കുന്നു. let, const എന്നിവ ബ്ലോക്ക് സ്കോപ്പ് (block-scoped) ഉള്ളവയാണ്. അവ കർലി ബ്രേസുകളെ ബഹുമാനിക്കുകയും ബ്ലോക്ക് അവസാനിക്കുമ്പോൾ അപ്രത്യക്ഷമാവുകയും ചെയ്യുന്നു.

ഹോയിസ്റ്റിംഗ് (hoisting) അവയെ കൈകാര്യം ചെയ്യുന്ന രീതിയിലും സൂക്ഷ്മവും എന്നാൽ നിർണ്ണായകവുമായ വ്യത്യാസമുണ്ട്. var ഡിക്ലറേഷനുകൾ ഹോയിസ്റ്റ് ചെയ്യപ്പെടുകയും ഉടൻ തന്നെ undefined ഉപയോഗിച്ച് ഇനീഷ്യലൈസ് ചെയ്യപ്പെടുകയും ചെയ്യുന്നു. let, const എന്നിവയും സാങ്കേതികമായി ഹോയിസ്റ്റ് ചെയ്യപ്പെടുന്നു; ഡിക്ലറേഷൻ വരിയിൽ എത്തുന്നതിന് മുമ്പ് തന്നെ അവ നിലനിൽക്കുന്നുണ്ടെന്ന് എൻജിൻ മനസ്സിലാക്കുന്നു. എന്നാൽ അവ ഇനീഷ്യലൈസ് ചെയ്യപ്പെടുന്നില്ല. അവ ടെമ്പറൽ ഡെഡ് സോൺ (temporal dead zone) എന്ന് വിളിക്കപ്പെടുന്ന ഒരു അവസ്ഥയിൽ നിൽക്കുന്നു. ഡിക്ലറേഷൻ വരി പ്രവർത്തിക്കുന്നതിന് മുമ്പ് അവ വായിക്കാൻ ശ്രമിച്ചാൽ, ഒരു രഹസ്യ undefined-ന് പകരം നിങ്ങൾക്ക് ഒരു കടുത്ത ReferenceError ലഭിക്കും. ആ ക്രാഷ് യഥാർത്ഥത്തിൽ സഹായകരമാണ്. ഇനീഷ്യലൈസ് ചെയ്യാത്ത ഡാറ്റയുടെ അടിസ്ഥാനത്തിൽ ലോജിക് മുന്നോട്ട് പോകുന്നത് അത് തടയുന്നു.

ലെക്സിക്കൽ സ്കോപ്പും ക്ലോഷറുകളും (Lexical Scope and Closures)

സ്കോപ്പ് ഒരു ലളിതമായ ചോദ്യത്തിന് ഉത്തരം നൽകുന്നു: എനിക്ക് ഈ വേരിയബിൾ എവിടെ നിന്ന് ഉപയോഗിക്കാം? ജാവാസ്ക്രിപ്റ്റ് ലെക്സിക്കൽ സ്കോപ്പ് (lexical scope) ആണ് ഉപയോഗിക്കുന്നത്, അതായത് ഒരു ഫങ്ക്ഷന്റെ ആക്സസ് അവകാശങ്ങൾ തീരുമാനിക്കപ്പെടുന്നത് അത് സോഴ്സ് കോഡിൽ എവിടെ എഴുതപ്പെട്ടു എന്നതിനെ അടിസ്ഥാനമാക്കിയാണ്, അല്ലാതെ അത് എവിടെ നിന്ന് വിളിക്കപ്പെടുന്നു എന്നതിനല്ല. ഒരു ഫങ്ക്ഷനുള്ളിൽ മറ്റൊരു ഫങ്ക്ഷൻ നിർവചിച്ചാൽ, ഉള്ളിലുള്ള ഫങ്ക്ഷന് അതിന്റെ പാരന്റ് ഫങ്ക്ഷനിലെ വേരിയബിളുകൾ വായിക്കാൻ സാധിക്കും. എന്നാൽ പുറത്തുള്ള ഫങ്ക്ഷന് ഉള്ളിലുള്ളവയിലേക്ക് പ്രവേശിക്കാൻ കഴിയില്ല. ഈ ബന്ധം സ്റ്റാറ്റിക് ആണ്. നിങ്ങൾക്ക് ആ ഇന്റർ ഫങ്ക്ഷനെ മറ്റ് മോഡ്യൂളുകളിലേക്ക് മാറ്റാനോ, ഒരു ഗ്ലോബൽ വേരിയബിളിൽ സൂക്ഷിക്കാനോ, തികച്ചും വ്യത്യസ്തമായ ഒരു ഫയലിൽ നിന്ന് വിളിക്കാനോ സാധിക്കും. അത് ജനിച്ച സ്കോപ്പിലെ വേരിയബിളുകളെ ഇപ്പോഴും ഓർമ്മിച്ചുവെക്കും.

ഈ സ്വഭാവം സ്വാഭാവികമായും ക്ലോഷറുകൾക്ക് (closures) കാരണമാകുന്നു. ഒരു ഇന്റർ ഫങ്ക്ഷൻ അതിന്റെ ഔട്ടർ സ്കോപ്പിലെ ഒരു വേരിയബിളിനെ റഫറൻസ് ചെയ്യുന്നതിലൂടെയാണ് ഒരു ക്ലോഷർ രൂപപ്പെടുന്നത്. ഔട്ടർ ഫങ്ക്ഷൻ അതിന്റെ പ്രവർത്തനം പൂർത്തിയാക്കി അതിന്റെ ലോക്കൽ വേരിയബിളുകൾ ഗാർബേജ് കളക്ഷൻ ചെയ്യപ്പെടേണ്ടി വന്നാലും, ഇന്റർ ഫങ്ക്ഷന് അവ ആവശ്യമായതുകൊണ്ട് ജാവാസ്ക്രിപ്റ്റ് അവ മെമ്മറിയിൽ നിലനിർത്തുന്നു. ഇന്റർ ഫങ്ക്ഷൻ അതിന്റെ ചുറ്റുമുള്ള പരിസ്ഥിതിയെ കൂടെ കൊണ്ടുപോകുന്നു.

ഇതൊരു കേവലമായ അക്കാദമിക് വിവരം മാത്രമല്ല. എക്സ്പ്ലിസിറ്റ് ആക്സസ് മോഡിഫയറുകൾ ഇല്ലാത്ത ഒരു ഭാഷയിൽ പ്രൈവറ്റ് സ്റ്റേറ്റ് (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 എന്നത് ഒരു ഫംഗ്ഷൻ എവിടെ എഴുതിയിരിക്കുന്നു എന്നതിനാലല്ല തീരുമാനിക്കപ്പെടുന്നത്. മറിച്ച്, ആ ഫംഗ്ഷൻ എങ്ങനെയാണ് വിളിക്കപ്പെടുന്നത് (call ചെയ്യപ്പെടുന്നത്) എന്നതിനെ അടിസ്ഥാനമാക്കിയാണ് അത് തീരുമാനിക്കപ്പെടുന്നത്.

Default binding സംഭവിക്കുന്നത് നിങ്ങൾ ഒരു സാധാരണ, സ്വതന്ത്രമായ ഫംഗ്ഷൻ വിളിക്കുമ്പോഴാണ്. Non-strict mode-ൽ, this ഗ്ലോബൽ ഒബ്ജക്റ്റിലേക്ക് (global object) മാറുന്നു. ഒരു ബ്രൗസറിൽ അത് window ആണ്. ഒരു കോൺടെക്സ്റ്റും ഇല്ലാതെ ഒരു ഫംഗ്ഷൻ വിളിച്ചാൽ, നിങ്ങൾ അറിയാതെ തന്നെ ഗ്ലോബൽ സ്റ്റേറ്റിനെ ബാധിച്ചേക്കാം.

Implicit binding സംഭവിക്കുന്നത് നിങ്ങൾ ഒരു ഫംഗ്ഷനെ ഒരു ഒബ്ജക്റ്റിലെ മെത്തേഡ് ആയി വിളിക്കുമ്പോഴാണ്. നിങ്ങൾ user.sayName() എന്ന് എഴുതിയാൽ, ആ കോൾ നടക്കുന്ന സമയത്തോളം this-നെ user ആയി സെറ്റ് ചെയ്യാൻ ഡോട്ട് (.) ചിഹ്നം എഞ്ചിനോട് ആവശ്യപ്പെടുന്നു. ഫംഗ്ഷൻ എവിടെ നിർവചിച്ചിരിക്കുന്നു എന്നതിനേക്കാൾ അത് എവിടെ നിന്ന് വിളിക്കുന്നു എന്നതിനാണ് ഇവിടെ പ്രാധാന്യം.

Explicit binding ഉപയോഗിച്ച് നിങ്ങൾക്ക് എല്ലാം മാനുവലായി നിയന്ത്രിക്കാം. call() ഉം apply() ഉം ഒരു ഫംഗ്ഷനെ ഉടൻ തന്നെ പ്രവർത്തിപ്പിക്കുകയും, ഒപ്പം this നിങ്ങൾ നൽകുന്ന ഒരു പ്രത്യേക ഒബ്ജക്റ്റായിരിക്കണം എന്ന് നിർബന്ധിക്കുകയും ചെയ്യുന്നു. ഇവ തമ്മിലുള്ള ഏക വ്യത്യാസം ആർഗ്യുമെന്റുകൾ കൈമാറുന്ന രീതിയാണ്: call കോമ ഉപയോഗിച്ചുള്ള ലിസ്റ്റ് സ്വീകരിക്കുമ്പോൾ, apply ഒരു അറേ (array) ആണ് സ്വീകരിക്കുന്നത്. bind() വ്യത്യസ്തമായി പ്രവർത്തിക്കുന്നു. ഇത് ഫംഗ്ഷനെ ഉടൻ തന്നെ പ്രവർത്തിപ്പിക്കില്ല. പകരം, നിങ്ങൾ നൽകിയ മൂല്യത്തിലേക്ക് this-നെ സ്ഥിരമായി ബന്ധിപ്പിച്ചുകൊണ്ട് ഒരു പുതിയ ഫംഗ്ഷനെ ഇത് തിരികെ നൽകുന്നു. കോൾബാക്കുകൾ (callbacks) കൈമാറുമ്പോൾ അവയുടെ കോൺടെക്സ്റ്റ് നഷ്ടപ്പെടാതിരിക്കാൻ ഇത് വളരെ ഉപകാരപ്രദമാണ്.

New binding എന്നത് ഒരു ഫംഗ്ഷൻ കോളിന് മുന്നിൽ new എന്ന കീവേഡ് ഉപയോഗിക്കുമ്പോഴാണ് സംഭവിക്കുന്നത്. എഞ്ചിൻ ഒരു പുതിയ ശൂന്യമായ ഒബ്ജക്റ്റ് നിർമ്മിക്കുകയും, അതിന്റെ പ്രോട്ടോടൈപ്പ് ലിങ്കേജ് സെറ്റ് ചെയ്യുകയും, കൺസ്ട്രക്ടറിനുള്ളിലെ this-നെ ആ പുതിയ ഇൻസ്റ്റൻസിലേക്ക് തിരിച്ചുവിടുകയും ചെയ്യുന്നു.

നിലനിൽക്കുന്ന കോഡ് എഴുതുക

മെക്കാനിക്സ് മനസ്സിലാക്കുന്നത് ഒരു കലയുടെ പകുതി മാത്രമാണ്. ബാക്കി പകുതി എന്നത് ആറ് മാസം കഴിഞ്ഞാലും മനുഷ്യർക്ക് വായിക്കാൻ കഴിയുന്ന രീതിയിൽ കോഡ് എഴുതുക എന്നതാണ്.

DRY (Don't Repeat Yourself) എന്നത് കേൾക്കുമ്പോൾ ലളിതമായി തോന്നാമെങ്കിലും, ഇത് പലപ്പോഴും ലംഘിക്കപ്പെടാറുണ്ട്. മൂന്ന് വ്യത്യസ്ത ഫയലുകളിൽ ഒരേ വാലിഡേഷൻ ലോജിക്കോ അല്ലെങ്കിൽ ഒരേ രീതിയിലുള്ള API കോൾ പാറ്റേണോ നിങ്ങൾ എഴുതുന്നുണ്ടെങ്കിൽ, അത് വേർതിരിച്ച് ഒരു ഫംഗ്ഷനാക്കി മാറ്റുക. ഒരു ഏകീകൃത സ്രോതസ്സ് (one source of truth) ഉണ്ടെങ്കിൽ, ആവശ്യകതകൾ മാറുമ്പോൾ ഒരു സ്ഥലത്ത് മാത്രം മാറ്റം വരുത്തിയാൽ മതിയാകും, ഇത് വളരെയധികം സമയം ലാഭിക്കുന്നു.

KISS (Keep It Simple, Stupid) എന്നത് അനാവശ്യമായ സങ്കീർണ്ണതകൾ ഒഴിവാക്കാനുള്ള ഒരു മാർഗ്ഗമാണ്. നെസ്റ്റഡ് ടെർണറികളും (nested ternaries) വൺ-ലൈനർ ക്ലോഷറുകളും (one-liner closures) കാണാൻ ബുദ്ധിപരമായ കാര്യമായി തോന്നാമെങ്കിലും, അവ ഡിബഗ്ഗിംഗ് സമയത്ത് മണിക്കൂറുകൾ നഷ്ടപ്പെടുത്തും. ഞാൻ നേരത്തെ കാണിച്ച ക്ലോഷർ പാറ്റേൺ ശക്തമാണ്, എന്നാൽ സാധിക്കും എന്നതുകൊണ്ട് മാത്രം അഞ്ച് ലെവലുകളോളം നെസ്റ്റിംഗ് ചെയ്യുന്നത് ഒരു തെറ്റാണ്. ലളിതമായ കോഡ് ടീമിലെ മാറ്റങ്ങൾക്കും, പ്രൊഡക്ഷൻ പ്രശ്നങ്ങൾക്കും, അർദ്ധരാത്രിയിൽ പെട്ടെന്നുണ്ടാകുന്ന സാങ്കേതിക തകരാറുകൾക്കും മികച്ച പ്രതിരോധമാണ്.

യഥാർത്ഥ നേട്ടം

എക്സിക്യൂഷൻ പഠിക്കുന്നത്...