ਜ਼ਿਆਦਾਤਰ ਲੋਕ JavaScript ਦੀ ਸ਼ੁਰੂਆਤ ਚੀਜ਼ਾਂ ਬਣਾ ਕੇ ਕਰਦੇ ਹਨ। ਤੁਸੀਂ ਇੱਕ ਬਟਨ ਨੂੰ ਕਨੈਕਟ ਕਰਦੇ ਹੋ, ਕੁਝ ਡੇਟਾ ਫੈਚ ਕਰਦੇ ਹੋ, ਅਤੇ DOM ਨੂੰ ਬਦਲਦੇ ਹੋਏ ਦੇਖਦੇ ਹੋ। ਫਿਰ ਅਬਸਟਰੈਕਸ਼ਨਾਂ (abstractions) ਫੇਲ ਹੋਣ ਲੱਗਦੀਆਂ ਹਨ। ਅਜਿਹੇ ਬੱਗ (bugs) ਸਾਹਮਣੇ ਆਉਂਦੇ ਹਨ ਜਿਨ੍ਹਾਂ ਦਾ ਕੋਈ ਮਤਲਬ ਨਹੀਂ ਨਿਕਲਦਾ: ਵੇਰੀਏਬਲ (variables) ਆਪਣੇ ਡਿਕਲੇਰੇਸ਼ਨ ਤੋਂ ਪਹਿਲਾਂ ਹੀ ਮੌਜੂਦ ਹੁੰਦੇ ਹਨ, ਫੰਕਸ਼ਨ ਉਹਨਾਂ ਵੇਰੀਏਬਲਾਂ ਨੂੰ ਯਾਦ ਰੱਖਦੇ ਹਨ ਜਿਨ੍ਹਾਂ ਤੱਕ ਉਹਨਾਂ ਦੀ ਪਹੁੰਚ ਨਹੀਂ ਹੋਣੀ ਚਾਹੀਦੀ, ਅਤੇ this ਕੀਵਰਡ ਵਿੰਡੋ (window), ਇੱਕ ਬਟਨ, ਜਾਂ ਕੁਝ ਵੀ ਨਹੀਂ ਵੱਲ ਇਸ਼ਾਰਾ ਕਰਦਾ ਹੈ। ਇਹ ਆਮ ਤੌਰ 'ਤੇ ਉਹ ਪਲ ਹੁੰਦਾ ਹੈ ਜਦੋਂ ਤੁਹਾਨੂੰ ਅਹਿਸਾਸ ਹੁੰਦਾ ਹੈ ਕਿ ਤੁਹਾਨੂੰ ਇੰਜਣ ਦੇ ਅੰਦਰਲੇ ਕੰਮ ਨੂੰ ਸਮਝਣ ਦੀ ਲੋੜ ਹੈ।

Execution Context: ਦੋ-ਪੜਾਵੀ ਸੈੱਟਅੱਪ

ਜਦੋਂ ਤੁਹਾਡੇ ਬ੍ਰਾਊਜ਼ਰ ਜਾਂ Node.js ਵਿੱਚ JavaScript ਇੰਜਣ ਕਿਸੇ ਸਕ੍ਰਿਪਟ ਦਾ ਸਾਹਮਣਾ ਕਰਦਾ ਹੈ, ਤਾਂ ਇਹ ਕਿਸੇ ਵਿਅਕਤੀ ਵਾਂਗ ਪੰਨੇ ਨੂੰ ਉੱਪਰ ਤੋਂ ਹੇਠਾਂ ਤੱਕ ਸਿਰਫ਼ ਪੜ੍ਹਦਾ ਨਹੀਂ ਹੈ। ਇਸ ਦੀ ਬਜਾਏ, ਇਹ ਇੱਕ execution context ਬਣਾਉਂਦਾ ਹੈ, ਜੋ ਇੱਕ ਕੰਟੇਨਰ ਹੈ ਜਿਸ ਵਿੱਚ ਕੋਡ ਦੇ ਇੱਕ ਖਾਸ ਹਿੱਸੇ ਨੂੰ ਚਲਾਉਣ ਲਈ ਲੋੜੀਂਦੀਆਂ ਸਾਰੀਆਂ ਚੀਜ਼ਾਂ ਹੁੰਦੀਆਂ ਹਨ। ਹਰ execution context ਦੋ ਵੱਖ-ਵੱਖ ਪੜਾਵਾਂ ਵਿੱਚੋਂ ਲੰਘਦਾ ਹੈ।

Memory Creation Phase. ਇਸ ਸ਼ੁਰੂਆਤੀ ਪਾਸ ਦੌਰਾਨ, ਇੰਜਣ ਪੂਰੇ scope ਨੂੰ ਸਕੈਨ ਕਰਦਾ ਹੈ ਅਤੇ ਹਰ ਵੇਰੀਏਬਲ ਅਤੇ ਫੰਕਸ਼ਨ ਡਿਕਲੇਰੇਸ਼ਨ ਲਈ ਮੈਮੋਰੀ ਅਲਾਟ ਕਰਦਾ ਹੈ। ਜੇਕਰ ਇਹ var ਦੇਖਦਾ ਹੈ, ਤਾਂ ਇਹ ਜਗ੍ਹਾ ਰਾਖਵੀਂ ਰੱਖਦਾ ਹੈ ਅਤੇ ਇੱਕ ਪਲੇਸਹੋਲਡਰ ਵਜੋਂ undefined ਨੂੰ ਸਟੋਰ ਕਰਦਾ ਹੈ। ਜੇਕਰ ਇਹ ਇੱਕ ਫੰਕਸ਼ਨ ਡਿਕਲੇਰੇਸ਼ਨ ਦੇਖਦਾ ਹੈ, ਤਾਂ ਇਹ ਪੂਰਾ ਫੰਕਸ਼ਨ ਬਾਡੀ ਸਟੋਰ ਕਰਦਾ ਹੈ। ਇਹੀ ਕਾਰਨ ਹੈ ਕਿ ਰਵਾਇਤੀ function ਕੀਵਰਡ ਨਾਲ ਡਿਕਲੇਅਰ ਕੀਤਾ ਗਿਆ ਫੰਕਸ਼ਨ ਉਸੇ scope ਵਿੱਚ ਪਹਿਲਾਂ ਦਿਖਾਈ ਦੇਣ ਵਾਲੀਆਂ ਲਾਈਨਾਂ ਤੋਂ ਕਾਲ ਕੀਤਾ ਜਾ ਸਕਦਾ ਹੈ। ਇੰਜਣ ਇਸ ਨੂੰ ਚਲਾਉਣਾ ਸ਼ੁਰੂ ਕਰਨ ਤੋਂ ਪਹਿਲਾਂ ਹੀ ਇਸ ਬਾਰੇ ਜਾਣਦਾ ਹੈ।

Code Execution Phase. ਹੁਣ ਇੰਜਣ ਤੁਹਾਡੇ ਕੋਡ ਨੂੰ ਲਾਈਨ-ਦਰ-ਲਾਈਨ ਚਲਾਉਂਦਾ ਹੈ। ਅਸਾਈਨਮੈਂਟਸ (Assignments) ਇੱਥੇ ਹੁੰਦੀਆਂ ਹਨ। ਐਕਸਪ੍ਰੈਸ਼ਨਸ (Expressions) ਦਾ ਮੁਲਾਂਕਣ ਕੀਤਾ ਜਾਂਦਾ ਹੈ। ਫੰਕਸ਼ਨਾਂ ਨੂੰ ਇਵੋਕ (invoke) ਕੀਤਾ ਜਾਂਦਾ ਹੈ। ਜੇਕਰ ਤੁਸੀਂ var name = "Alice"; ਲਿਖਿਆ ਹੈ, ਤਾਂ ਪਹਿਲੇ ਪੜਾਅ ਦਾ ਪਲੇਸਹੋਲਡਰ ਅੰਤ ਵਿੱਚ ਸਟ੍ਰਿੰਗ ਨਾਲ ਬਦਲ ਦਿੱਤਾ ਜਾਂਦਾ ਹੈ। ਇਸ ਦੋ-ਪਾਸ (two-pass) ਵਿਵਹਾਰ ਨੂੰ ਸਮਝਣ ਨਾਲ ਬਹੁਤ ਸਾਰੀ ਉਲਝਣ ਦੂਰ ਹੋ ਜਾਂਦੀ ਹੈ। ਇੰਜਣ ਲਾਪਰਵਾਹ ਨਹੀਂ ਹੈ; ਇਹ ਇੱਕ ਸਖ਼ਤ ਸੈੱਟਅੱਪ ਰੁਟੀਨ ਦੀ ਪਾਲਣਾ ਕਰ ਰਿਹਾ ਹੈ।

Variables ਅਤੇ Temporal Dead Zone

var, let, ਅਤੇ const ਵਿੱਚੋਂ ਚੋਣ ਕਰਨਾ ਸਿਰਫ਼ ਇੱਕ ਸ਼ੈਲੀ ਦੀ ਪਸੰਦ ਨਹੀਂ ਹੈ। var ਫੰਕਸ਼ਨ-ਸਕੋਪਡ (function-scoped) ਹੁੰਦਾ ਹੈ, ਜਿਸਦਾ ਮਤਲਬ ਹੈ ਕਿ ਇਹ ਬ੍ਰੇਸਿਸ (braces) ਨੂੰ ਪੂਰੀ ਤਰ੍ਹਾਂ ਨਜ਼ਰਅੰਦਾਜ਼ ਕਰ ਦਿੰਦਾ ਹੈ। ਇਸ ਨੂੰ ਇੱਕ if ਬਲਾਕ ਦੇ ਅੰਦਰ ਡਿਕਲੇਅਰ ਕਰੋ, ਅਤੇ ਇਹ ਬਾਹਰ ਨਿਕਲ ਆਉਂਦਾ ਹੈ। ਇਹ ਵਿਵਹਾਰ ਸ਼ੁਰੂਆਤੀ JavaScript ਵਿੱਚ ਮਤਲਬ ਰੱਖ ਸਕਦਾ ਸੀ, ਪਰ ਆਧੁਨਿਕ ਐਪਲੀਕੇਸ਼ਨਾਂ ਵਿੱਚ ਇਹ ਰੱਖ-ਰਖਾਅ (maintenance) ਦੀਆਂ ਮੁਸ਼ਕਲਾਂ ਪੈਦਾ ਕਰਦਾ ਹੈ। let ਅਤੇ const ਬਲਾਕ-ਸਕੋਪਡ (block-scoped) ਹੁੰਦੇ ਹਨ। ਉਹ ਕਰਲੀ ਬ੍ਰੇਸਿਸ (curly braces) ਦਾ ਸਤਿਕਾਰ ਕਰਦੇ ਹਨ ਅਤੇ ਜਦੋਂ ਬਲਾਕ ਖਤਮ ਹੁੰਦਾ ਹੈ ਤਾਂ ਗਾਇਬ ਹੋ ਜਾਂਦੇ ਹਨ।

ਹੋਇਸਟਿੰਗ (hoisting) ਉਹਨਾਂ ਨਾਲ ਕਿਵੇਂ ਵਿਵਹਾਰ ਕਰਦੀ ਹੈ, ਇਸ ਵਿੱਚ ਇੱਕ ਸੂਖਮ ਪਰ ਮਹੱਤਵਪੂਰਨ ਅੰਤਰ ਵੀ ਹੈ। var ਡਿਕਲੇਰੇਸ਼ਨਾਂ ਨੂੰ ਹੋਇਸਟ ਕੀਤਾ ਜਾਂਦਾ ਹੈ ਅਤੇ ਤੁਰੰਤ undefined ਨਾਲ ਸ਼ੁਰੂ (initialize) ਕਰ ਦਿੱਤਾ ਜਾਂਦਾ ਹੈ। let ਅਤੇ const ਤਕਨੀਕੀ ਤੌਰ 'ਤੇ ਵੀ ਹੋਇਸਟ ਕੀਤੇ ਜਾਂਦੇ ਹਨ; ਇੰਜਣ ਡਿਕਲੇਰੇਸ਼ਨ ਲਾਈਨ ਤੱਕ ਪਹੁੰਚਣ ਤੋਂ ਪਹਿਲਾਂ ਹੀ ਜਾਣਦਾ ਹੈ ਕਿ ਉਹ ਮੌਜੂਦ ਹਨ। ਪਰ ਉਹ ਸ਼ੁਰੂ (initialize) ਨਹੀਂ ਕੀਤੇ ਜਾਂਦੇ। ਉਹ ਇੱਕ ਅਜਿਹੀ ਅਵਸਥਾ ਵਿੱਚ ਰਹਿੰਦੇ ਹਨ ਜਿਸ ਨੂੰ temporal dead zone ਕਿਹਾ ਜਾਂਦਾ ਹੈ। ਜੇਕਰ ਤੁਸੀਂ ਡਿਕਲੇਰੇਸ਼ਨ ਲਾਈਨ ਚੱਲਣ ਤੋਂ ਪਹਿਲਾਂ ਉਹਨਾਂ ਨੂੰ ਪੜ੍ਹਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰਦੇ ਹੋ, ਤਾਂ ਤੁਹਾਨੂੰ ਇੱਕ ਚੁਪਚਾਪ undefined ਦੀ ਬਜਾਏ ਇੱਕ ਸਖ਼ਤ ReferenceError ਮਿਲੇਗਾ। ਉਹ ਕ੍ਰੈਸ਼ (crash) ਅਸਲ ਵਿੱਚ ਮਦਦਗਾਰ ਹੈ। ਇਹ ਅਣ-ਸ਼ੁਰੂ ਕੀਤੇ (uninitialized) ਡੇਟਾ ਦੇ ਆਧਾਰ 'ਤੇ ਲੌਜਿਕ ਨੂੰ ਅੱਗੇ ਵਧਣ ਤੋਂ ਰੋਕਦਾ ਹੈ।

Lexical Scope ਅਤੇ Closures

ਸਕੋਪ (Scope) ਇੱਕ ਸਧਾਰਨ ਸਵਾਲ ਦਾ ਜਵਾਬ ਦਿੰਦਾ ਹੈ: ਮੈਂ ਇਸ ਵੇਰੀਏਬਲ ਤੱਕ ਕਿੱਥੇ ਪਹੁੰਚ ਸਕਦਾ ਹਾਂ? JavaScript ਲੈਕਸੀਕਲ ਸਕੋਪ (lexical scope) ਦੀ ਵਰਤੋਂ ਕਰਦਾ ਹੈ, ਜਿਸਦਾ ਮਤਲਬ ਹੈ ਕਿ ਫੰਕਸ਼ਨ ਦੇ ਪਹੁੰਚ ਅਧਿਕਾਰ ਇਸ ਗੱਲ ਨਾਲ ਤੈਅ ਹੁੰਦੇ ਹਨ ਕਿ ਉਹ ਸੋਰਸ ਕੋਡ ਵਿੱਚ ਸਰੀਰਕ ਤੌਰ 'ਤੇ ਕਿੱਥੇ ਲਿਖਿਆ ਗਿਆ ਸੀ, ਨਾ ਕਿ ਇਸ ਨਾਲ ਕਿ ਉਹ ਕਿੱਥੇ ਕਾਲ ਕੀਤਾ ਜਾਂਦਾ ਹੈ। ਜੇਕਰ ਤੁਸੀਂ ਇੱਕ ਫੰਕਸ਼ਨ ਨੂੰ ਦੂਜੇ ਫੰਕਸ਼ਨ ਦੇ ਅੰਦਰ ਡਿਫਾਈਨ ਕਰਦੇ ਹੋ, ਤਾਂ ਅੰਦਰਲਾ ਫੰਕਸ਼ਨ ਆਪਣੇ ਪੇਰੈਂਟ (parent) ਤੋਂ ਵੇਰੀਏਬਲ ਪੜ੍ਹਨ ਲਈ ਬਾਹਰ ਤੱਕ ਪਹੁੰਚ ਸਕਦਾ ਹੈ। ਬਾਹਰੀ ਫੰਕਸ਼ਨ ਅੰਦਰ ਤੱਕ ਨਹੀਂ ਪਹੁੰਚ ਸਕਦਾ। ਇਹ ਰਿਸ਼ਤਾ ਸਟੈਟਿਕ (static) ਹੈ। ਤੁਸੀਂ ਉਸ ਅੰਦਰਲੇ ਫੰਕਸ਼ਨ ਨੂੰ ਮੋਡਿਊਲਜ਼ (modules) ਵਿੱਚ ਭੇਜ ਸਕਦੇ ਹੋ, ਇਸਨੂੰ ਇੱਕ ਗਲੋਬਲ ਵੇਰੀਏਬਲ ਵਿੱਚ ਸਟੋਰ ਕਰ ਸਕਦੇ ਹੋ, ਅਤੇ ਇਸਨੂੰ ਪੂਰੀ ਤਰ੍ਹਾਂ ਵੱਖਰੀ ਫਾਈਲ ਤੋਂ ਕਾਲ ਕਰ ਸਕਦੇ ਹੋ। ਇਹ ਅਜੇ ਵੀ ਉਸ ਸਕੋਪ ਦੇ ਵੇਰੀਏਬਲਾਂ ਨੂੰ ਯਾਦ ਰੱਖਦਾ ਹੈ ਜਿੱਥੇ ਇਹ ਪੈਦਾ ਹੋਇਆ ਸੀ।

ਉਹ ਵਿਵਹਾਰ ਕੁਦਰਤੀ ਤੌਰ 'ਤੇ closures ਪੈਦਾ ਕਰਦਾ ਹੈ। ਇੱਕ closure ਉਦੋਂ ਬਣਦਾ ਹੈ ਜਦੋਂ ਇੱਕ ਅੰਦਰਲਾ ਫੰਕਸ਼ਨ ਆਪਣੇ ਬਾਹਰੀ ਸਕੋਪ ਦੇ ਵੇਰੀਏਬਲ ਦਾ ਰੈਫਰੈਂਸ (reference) ਰੱਖਦਾ ਹੈ। ਬਾਹਰੀ ਫੰਕਸ਼ਨ ਦੇ ਚੱਲਣ ਦੇ ਖਤਮ ਹੋਣ ਅਤੇ ਉਸਦੇ ਲੋਕਲ ਵੇਰੀਏਬਲਾਂ ਦੇ ਗਾਰਬੇਜ ਕਲੈਕਟ (garbage collected) ਹੋ ਜਾਣ ਤੋਂ ਬਾਅਦ ਵੀ, JavaScript ਉਹਨਾਂ ਨੂੰ ਮੈਮੋਰੀ ਵਿੱਚ ਬਚਾ ਕੇ ਰੱਖਦਾ ਹੈ ਕਿਉਂਕਿ ਅੰਦਰਲੇ ਫੰਕਸ਼ਨ ਨੂੰ ਅਜੇ ਵੀ ਉਹਨਾਂ ਦੀ ਲੋੜ ਹੁੰਦੀ ਹੈ। ਅੰਦਰਲਾ ਫੰਕਸ਼ਨ ਆਪਣੇ ਆਲੇ-ਦੁਆਲੇ ਦੇ ਵਾਤਾਵਰਣ ਨੂੰ ਆਪਣੇ ਨਾਲ ਲੈ ਕੇ ਚੱਲਦਾ ਹੈ।

ਇਹ ਸਿਰਫ਼ ਇੱਕ ਅਕਾਦਮਿਕ ਵੇਰਵਾ ਨਹੀਂ ਹੈ। Closures ਤੁਹਾਨੂੰ ਇੱਕ ਅਜਿਹੀ ਭਾਸ਼ਾ ਵਿੱਚ ਪ੍ਰਾਈਵੇਟ ਸਟੇਟ (private state) ਬਣਾਉਣ ਦਾ ਵਿਹਾਰਕ ਤਰੀਕਾ ਦਿੰਦੇ ਹਨ ਜਿਸ ਵਿੱਚ ਸਪੱਸ਼ਟ ਐਕਸੈਸ ਮੋਡੀਫਾਇਰਜ਼ (access modifiers) ਦੀ ਘਾਟ ਹੈ।

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 ਪ੍ਰੈਕਟੀਸ ਵਿੱਚ

ਇਹ ਸੁਣਨਾ ਆਮ ਹੈ ਕਿ JavaScript "ਡਿਕਲੇਰੇਸ਼ਨਾਂ ਨੂੰ ਉੱਪਰ ਲੈ ਜਾਂਦਾ ਹੈ।" ਇਹ ਇੱਕ ਲਾਭਦਾਇਕ ਮਾਨਸਿਕ ਮਾਡਲ ਹੈ, ਪਰ ਕੋਡ ਅਸਲ ਵਿੱਚ ਦੁਬਾਰਾ ਨਹੀਂ ਲਿਖਿਆ ਜਾਂਦਾ। ਮੈਮਰੀ ਬਣਾਉਣ ਦੇ ਪੜਾਅ (memory creation phase) ਦੌਰਾਨ, ਇੰਜਣ ਐਗਜ਼ੀਕਿਊਸ਼ਨ ਸ਼ੁਰੂ ਹੋਣ ਤੋਂ ਪਹਿਲਾਂ ਸਿਰਫ਼ ਡਿਕਲੇਰੇਸ਼ਨਾਂ ਨੂੰ ਰਜਿਸਟਰ ਕਰਦਾ ਹੈ। var x = 5; ਵਰਗਾ ਸਟੇਟਮੈਂਟ ਇਸ ਤਰ੍ਹਾਂ ਵਿਵਹਾਰ ਕਰਦਾ ਹੈ ਜਿਵੇਂ ਕਿ ਡਿਕਲੇਰੇਸ਼ਨ ਅਤੇ ਇਨੀਸ਼ੀਅਲਾਈਜ਼ੇਸ਼ਨ (initialization) ਵੱਖਰੇ ਹੋਣ। var x; ਵਾਲੀ ਡਿਕਲੇਰੇਸ਼ਨ ਜਲਦੀ ਪ੍ਰੋਸੈਸ ਕੀਤੀ ਜਾਂਦੀ ਹੈ ਅਤੇ undefined ਵਜੋਂ ਇਨੀਸ਼ੀਅਲਾਈਜ਼ ਕੀਤੀ ਜਾਂਦੀ ਹੈ। ਅਸਾਈਨਮੈਂਟ x = 5; ਉੱਥੇ ਹੀ ਰਹਿੰਦੀ ਹੈ ਜਿੱਥੇ ਤੁਸੀਂ ਇਸਨੂੰ ਲਿਖਿਆ ਸੀ ਅਤੇ ਐਗਜ਼ੀਕਿਊਸ਼ਨ ਫੇਜ਼ ਦੌਰਾਨ ਚੱਲਦੀ ਹੈ।

ਇਸ ਕਾਰਨ, var ਹੈਰਾਨ ਕਰਨ ਵਾਲੇ ਨਤੀਜੇ ਦੇ ਸਕਦਾ ਹੈ। ਇੱਕ ਵੇਰੀਏਬਲ ਜੋ ਫੰਕਸ਼ਨ ਦੇ ਉੱਪਰਲੇ ਹਿੱਸੇ ਦੇ ਨੇੜੇ ਵਰਤਿਆ ਜਾਂਦਾ ਹੈ, ਉਹ undefined ਹੋ ਸਕਦਾ ਹੈ ਭਾਵੇਂ ਕਿ ਇੱਕ ਵੱਡਾ ਅਸਾਈਨਮੈਂਟ ਹੇਠਲੇ ਹਿੱਸੇ ਵਿੱਚ ਹੋਵੇ। let ਅਤੇ const ਦੀ ਵਰਤੋਂ ਕਰਨ ਨਾਲ ਉਹ ਖ਼ਾਸ ਖ਼ਤਰਨਾਕ ਗਲਤੀ (foot-gun) ਖ਼ਤਮ ਹੋ ਜਾਂਦੀ ਹੈ ਕਿਉਂਕਿ temporal dead zone ਤੁਹਾਨੂੰ ਆਪਣੀਆਂ ਡਿਕਲੇਰੇਸ਼ਨਾਂ ਨੂੰ ਵਰਤੋਂ ਤੋਂ ਉੱਪਰ ਰੱਖਣ ਲਈ ਮਜਬੂਰ ਕਰਦਾ ਹੈ।

this ਨੂੰ ਆਪਣੀ ਵੈਲਯੂ (Value) ਕਿਵੇਂ ਮਿਲਦੀ ਹੈ

ਜੇਕਰ ਐਗਜ਼ੀਕਿਊਸ਼ਨ ਕੰਟੈਕਸਟ (execution context) ਅਤੇ ਸਕੋਪ ਇਹ ਨਿਰਧਾਰਤ ਕਰਦੇ ਹਨ ਕਿ ਵੇਰੀਏਬਲ ਕਿੱਥੇ ਰਹਿੰਦੇ ਹਨ, ਤਾਂ this ਇਹ ਨਿਰਧਾਰਤ ਕਰਦਾ ਹੈ ਕਿ ਇਸ ਸਮੇਂ ਕਿਹੜਾ ਆਬਜੈਕਟ ਕੰਟਰੋਲ ਵਿੱਚ ਹੈ। ਲੈਕਸੀਕਲ ਵੇਰੀਏਬਲ ਦੇ ਉਲਟ, this ਇਹ ਫੈਸਲਾ ਨਹੀਂ ਕਰਦਾ ਕਿ ਫੰਕਸ਼ਨ ਕਿੱਥੇ ਲਿਖਿਆ ਗਿਆ ਹੈ। ਇਹ ਪੂਰੀ ਤਰ੍ਹਾਂ ਇਸ ਗੱਲ 'ਤੇ ਨਿਰਭਰ ਕਰਦਾ ਹੈ ਕਿ ਫੰਕਸ਼ਨ ਨੂੰ ਕਿਵੇਂ ਕਾਲ (call) ਕੀਤਾ ਗਿਆ ਹੈ।

Default binding ਉਦੋਂ ਹੁੰਦੀ ਹੈ ਜਦੋਂ ਤੁਸੀਂ ਇੱਕ ਸਾਧਾਰਨ, ਸਟੈਂਡਅਲੋਨ ਫੰਕਸ਼ਨ ਨੂੰ ਇਵੋਕ (invoke) ਕਰਦੇ ਹੋ। ਨਾਨ-ਸਟ੍ਰਿਕਟ ਮੋਡ ਵਿੱਚ, this ਗਲੋਬਲ ਆਬਜੈਕਟ 'ਤੇ ਫਾਲ ਬੈਕ (fall back) ਕਰ ਜਾਂਦਾ ਹੈ। ਬ੍ਰਾਊਜ਼ਰ ਵਿੱਚ, ਉਹ window ਹੈ। ਕਿਸੇ ਵੀ ਕੰਟੈਕਸਟ ਤੋਂ ਬਿਨਾਂ ਫੰਕਸ਼ਨ ਨੂੰ ਕਾਲ ਕਰੋ, ਅਤੇ ਤੁਸੀਂ ਅਣਜਾਣੇ ਵਿੱਚ ਗਲੋਬਲ ਸਟੇਟ ਨੂੰ ਛੂਹ ਸਕਦੇ ਹੋ।

Implicit binding ਉਦੋਂ ਹੁੰਦੀ ਹੈ ਜਦੋਂ ਤੁਸੀਂ ਕਿਸੇ ਆਬਜੈਕਟ ਦੇ ਮੈਥਡ ਵਜੋਂ ਫੰਕਸ਼ਨ ਨੂੰ ਕਾਲ ਕਰਦੇ ਹੋ। ਜੇਕਰ ਤੁਸੀਂ user.sayName() ਲਿਖਦੇ ਹੋ, ਤਾਂ ਡੌਟ (dot) ਚੁੱਪਚਾਪ ਇੰਜਣ ਨੂੰ ਉਸ ਕਾਲ ਦੌਰਾਨ this ਨੂੰ user 'ਤੇ ਸੈੱਟ ਕਰਨ ਲਈ ਕਹਿੰਦਾ ਹੈ। ਕਾਲ ਕਰਨ ਦੀ ਜਗ੍ਹਾ, ਡੈਫੀਨੇਸ਼ਨ (definition) ਦੀ ਜਗ੍ਹਾ ਨਾਲੋਂ ਜ਼ਿਆਦਾ ਮਾਇਨੇ ਰੱਖਦੀ ਹੈ।

Explicit binding ਤੁਹਾਨੂੰ ਸਭ ਕੁਝ ਮੈਨੂਅਲੀ ਓਵਰਰਾਈਡ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿੰਦੀ ਹੈ। call() ਅਤੇ apply() ਇੱਕ ਫੰਕਸ਼ਨ ਨੂੰ ਤੁਰੰਤ ਇਵੋਕ ਕਰਦੇ ਹਨ ਜਦੋਂ ਕਿ this ਨੂੰ ਤੁਹਾਡੇ ਦੁਆਰਾ ਦਿੱਤੇ ਗਏ ਇੱਕ ਖਾਸ ਆਬਜੈਕਟ ਵਜੋਂ ਫੋਰਸ ਕਰਦੇ ਹਨ। ਉਹਨਾਂ ਵਿਚਕਾਰ ਇਕਲੌਤਾ ਅੰਤਰ ਇਹ ਹੈ ਕਿ ਆਰਗੂਮੈਂਟਸ (arguments) ਕਿਵੇਂ ਪਾਸ ਕੀਤੇ ਜਾਂਦੇ ਹਨ: call ਇੱਕ ਕਾਮਾ-ਵੱਖਰੀ ਲਿਸਟ ਲੈਂਦਾ ਹੈ, ਜਦੋਂ ਕਿ apply ਇੱਕ ਐਰੇ (array) ਲੈਂਦਾ ਹੈ। bind() ਵੱਖਰੇ ਤਰੀਕੇ ਨਾਲ ਕੰਮ ਕਰਦਾ ਹੈ। ਇਹ ਫੰਕਸ਼ਨ ਨੂੰ ਤੁਰੰਤ ਇਵੋਕ ਨਹੀਂ ਕਰਦਾ। ਇਸ ਦੀ ਬਜਾਏ, ਇਹ ਇੱਕ ਨਵਾਂ ਫੰਕਸ਼ਨ ਵਾਪਸ ਕਰਦਾ ਹੈ ਜਿਸ ਵਿੱਚ this ਪੱਕੇ ਤੌਰ 'ਤੇ ਤੁਹਾਡੇ ਦੁਆਰਾ ਦਿੱਤੀ ਗਈ ਵੈਲਯੂ ਨਾਲ ਲੌਕ ਹੁੰਦਾ ਹੈ। ਇਹ ਉਹਨਾਂ ਕਾਲਬੈਕਸ (callbacks) ਲਈ ਬਹੁਤ ਕੀਮਤੀ ਹੈ ਜੋ ਹੋਰਨਾਂ ਸਮੇਂ ਦੌਰਾਨ ਆਪਣਾ ਕੰਟੈਕਸਟ ਗੁਆ ਸਕਦੇ ਹਨ।

New binding ਉਦੋਂ ਕੰਮ ਵਿੱਚ ਆਉਂਦੀ ਹੈ ਜਦੋਂ ਤੁਸੀਂ ਫੰਕਸ਼ਨ ਕਾਲ ਦੇ ਸਾਹਮਣੇ new ਕੀਵਰਡ ਦੀ ਵਰਤੋਂ ਕਰਦੇ ਹੋ। ਇੰਜਣ ਇੱਕ ਬਿਲਕੁਲ ਨਵਾਂ ਖਾਲੀ ਆਬਜੈਕਟ ਬਣਾਉਂਦਾ ਹੈ, ਇਸਦਾ ਪ੍ਰੋਟੋਟਾਈਪ ਲਿੰਕੇਜ (prototype linkage) ਸੈੱਟ ਕਰਦਾ ਹੈ, ਅਤੇ ਕੰਸਟਰਕਟਰ ਦੇ ਅੰਦਰ this ਨੂੰ ਉਸ ਨਵੇਂ ਇੰਸਟੈਂਸ (instance) ਵੱਲ ਇਸ਼ਾਰਾ ਕਰਦਾ ਹੈ।

ਟਿਕਾਊ ਕੋਡ ਲਿਖਣਾ

ਮਕੈਨਿਕਸ ਨੂੰ ਸਮਝਣਾ ਕਲਾ ਦਾ ਅੱਧਾ ਹਿੱਸਾ ਹੈ। ਦੂਜਾ ਅੱਧਾ ਹਿੱਸਾ ਅਜਿਹਾ ਕੋਡ ਲਿਖਣਾ ਹੈ ਜਿਸਨੂੰ ਇਨਸਾਨ ਭਵਿੱਖ ਵਿੱਚ ਛੇ ਮਹੀਨਿਆਂ ਬਾਅਦ ਵੀ ਪੜ੍ਹ ਸਕਣ।

DRY (Don't Repeat Yourself) ਸੁਣਨ ਵਿੱਚ ਸਪੱਸ਼ਟ ਲੱਗਦਾ ਹੈ, ਪਰ ਇਸਦਾ ਲਗਾਤਾਰ ਉਲੰਘਣ ਕੀਤਾ ਜਾਂਦਾ ਹੈ। ਜੇਕਰ ਤੁਸੀਂ ਆਪਣੇ ਆਪ ਨੂੰ ਤਿੰਨ ਵੱਖ-ਵੱਖ ਫਾਈਲਾਂ ਵਿੱਚ ਇੱਕੋ ਜਿਹੀ ਵੈਲੀਡੇਸ਼ਨ ਲੌਜਿਕ ਜਾਂ API ਕਾਲ ਪੈਟਰਨ ਲਿਖਦੇ ਹੋਏ ਪਾਉਂਦੇ ਹੋ, ਤਾਂ ਇਸਨੂੰ ਬਾਹਰ ਕੱਢੋ (extract)। ਇੱਕ ਫੰਕਸ਼ਨ ਲਿਖੋ। ਸੱਚ ਦਾ ਇੱਕ ਸਰੋਤ (One source of truth) ਹੋਣ ਦਾ ਮਤਲਬ ਹੈ ਕਿ ਜਦੋਂ ਲੋੜਾਂ ਬਦਲਦੀਆਂ ਹਨ ਤਾਂ ਅੱਪਡੇਟ ਕਰਨ ਲਈ ਇੱਕ ਹੀ ਜਗ੍ਹਾ ਹੁੰਦੀ ਹੈ, ਅਤੇ ਇਹ ਸਮੇਂ ਦੀ ਬਚਤ ਇਸ ਤਰੀਕੇ ਨਾਲ ਕਰਦਾ ਹੈ ਜਿਸਦਾ ਅੰਦਾਜ਼ਾ ਲਗਾਉਣਾ ਮੁਸ਼ਕਲ ਹੈ।

KISS (Keep It Simple, Stupid) ਅਹੰਕਾਰ ਦੇ ਵਿਰੁੱਧ ਇੱਕ ਰੱਖਿਆ ਹੈ। ਨੇਸਟਡ ਟੈਰਨਰੀਜ਼ (Nested ternaries) ਅਤੇ ਵਨ-ਲਾਈਨਰ ਕਲੋਜ਼ਰਸ (one-liner closures) ਚਲਾਕ ਲੱਗਦੇ ਹਨ, ਪਰ ਡੀਬੱਗਿੰਗ ਦੌਰਾਨ ਇਹਨਾਂ 'ਤੇ ਕਈ ਘੰਟੇ ਖਰਚੇ ਜਾਂਦੇ ਹਨ। ਜੋ ਕਲੋਜ਼ਰ ਪੈਟਰਨ ਮੈਂ ਪਹਿਲਾਂ ਦਿਖਾਇਆ ਸੀ ਉਹ ਸ਼ਕਤੀਸ਼ਾਲੀ ਹੈ, ਫਿਰ ਵੀ ਸਿਰਫ਼ ਇਸ ਲਈ ਕਿਉਂਕਿ ਤੁਸੀਂ ਕਰ ਸਕਦੇ ਹੋ ਪੰਜ ਪੱਧਰ ਡੂੰਘਾ ਨੇਸਟ ਕਰਨਾ ਇੱਕ ਗਲਤੀ ਹੈ। ਸਾਦਾ ਕੋਡ ਟੀਮ ਦੇ ਬਦਲਾਅ, ਪ੍ਰੋਡਕਸ਼ਨ ਘਟਨਾਵਾਂ, ਅਤੇ ਰਾਤ ਦੇ ਵਿਚਕਾਰ ਆਉਣ ਵਾਲੇ ਉਹਨਾਂ ਅਲਰਟਾਂ ਵਿੱਚ ਬਚ ਜਾਂਦਾ ਹੈ ਜਦੋਂ ਕੁਝ ਟੁੱਟ ਜਾਂਦਾ ਹੈ ਅਤੇ ਕਿਸੇ ਨੂੰ ਯਾਦ ਨਹੀਂ ਹੁੰਦਾ ਕਿ ਕਿਉਂ।

ਅਸਲ ਫਾਇਦਾ

ਐਗਜ਼ੀਕਿਊਸ਼ਨ ਦਾ ਅਧਿਐਨ ਕਰਨਾ