Skip to content

Chapter 22: JavaScript This Context

22-1: মোস্ট কনফিউজিং this

নতুন নতুন প্রেমে পড়লে পোলাপান শয়নে-স্বপনে-জাগরণে তাকে দেখতে পায়। একই অবস্থা হয় কিছুদিন জাভাস্ক্রিপ্ট ইউজ করার পর। একদম শয়নে-স্বপনে-জাগরণে এমনকি আগানে-বাগানে-চিপাচাপায়— সব জায়গায় this কি-ওয়ার্ড দেখতে পায়।

শুধু দেখলে সমস্যা ছিল না। দেখতে গিয়ে একেক জায়গায় তার একেক রূপ, একেক মোহ আর একেক আচরণ দেখে পোলাপান ফিদা হওয়ার পাশাপাশি কনফিউজডও হতে শুরু করে। this আসলে কী জিনিস?

this একটা ইংরেজি শব্দ। যার মানে হচ্ছে এই, এটা ইত্যাদি। আরেকটু স্পেসিফিকভাবে বললে বলা যায়— this বলতে বুঝায় 'এটার' বা এই অবজেক্টের।

this হলো জাভাস্ক্রিপ্টের একটা বিশেষ কি-ওয়ার্ড, যেটা একেক সময় একেক ধরনের অবজেক্টকে রেফার করে। কারণ, this-এর ভ্যালু ডিপেন্ড করে কোথায় এবং কীভাবে— এটা ব্যবহার করা হচ্ছে তার ওপর। তাই বিভিন্ন কনটেক্সটে this-এর বিহেভিয়ারের কয়েকটা উদাহরণ দেখে ফেলি।

1. Global Context (বা window object)

তুই যদি ব্রাউজারে গিয়ে this-কে কনসোল লগ করিস, তাহলে তোকে window অবজেক্ট দেখাবে। এইটা হচ্ছে ব্রাউজারে জাভাস্ক্রিপ্টের সব জিনিস ধরে রাখে। একদম মেইন জিনিস দেখাবে।

javascript
console.log(this);

Output:
Window {0: global, window: Window, document: document, …}

এইটাকে ক্ষেত্রবিশেষে window, this, global this এইসব নামে ডাকে। গ্লোবাল অবজেক্ট হলো এমন একটা অবজেক্ট, যেটা সব গ্লোবাল ভেরিয়েবল, ফাংশন এবং প্রোপার্টিগুলোকে ধরে রাখে। ওভারঅল জাভাস্ক্রিপ্ট কীভাবে চলবে, সেটা ঠিক করে এবং যেখানেই জাভাস্ক্রিপ্ট চলুক না কেন, সেখানেই একটা গ্লোবাল অবজেক্ট থাকবেই। এইটাই মেইন জিনিস।

2. অবজেক্টের মেথডের মধ্যে

যখন "this" অবজেক্টের মধ্যে কোনো মেথড ব্যবহার করা হয়, তখন এটা সেই অবজেক্টকেই নির্দেশ করে। ধর, নিচের মতো অবজেক্ট আছে—

javascript
const player = {
    name: 'Sakep',
    score: 100,
    showScore() {
        console.log(this);
    }
};

player.showScore();

Output: {name: 'Sakep', score: 100, showScore: ƒ}

showScore মেথডের মধ্যে "this" বলতে player অবজেক্টকে বুঝায়। কারণ, মেথডটা অবজেক্টের মধ্যেই আছে।

যেহেতু this বলতে সেই অবজেক্টকে বুঝায়, তাই আমরা সেই অবজেক্টের মধ্যে যদি কোনো প্রোপার্টি বা মেথডকে এক্সেস করতে চাই, তখন আমরা this অবজেক্টের ওপরে সেই অবজেক্টের প্রোপার্টি বা মেথডকে এক্সেস করি। যেমন, আমি যদি showScore-এর ভিতরে name বা score এক্সেস করতে চাই, তাহলে this-এর পর ডট চিহ্ন দিয়ে তারপর সেই প্রোপার্টির নাম লিখব। নিচের মতো করে—

javascript
const player = {
  name: "Sakep",
  score: 100,
  showScore() {
    console.log(this.score);
  },
};

player.showScore();

Output: 100;

player অবজেক্টের যেকোনো মেথডের ভিতরে "this.name" মানে player অবজেক্টের name প্রোপার্টির মান। একইভাবে মানে "this.score" মানে player অবজেক্টের score প্রোপার্টির মান।

3. class-এর মধ্যে

ES6-এর class-এর ভিতরে যখন আমরা this ইউজ করি, সেটা সেই ক্লাসকে না; বরং সেই ক্লাস থেকে যেসব অবজেক্ট তৈরি হবে, তাদেরকে বুঝায়। সেজন্য কোনো ক্লাসের মধ্যে this.name বলতে বুঝায় সেই ক্লাস থেকে যেসব অবজেক্ট তৈরি হবে, তাদের name প্রোপার্টিকে বুঝায় [জিনিসটা অবজেক্টের ভিতরে this-এর মতোই বলতে পারিস। তবে টেকনিক্যালি বললে হালকা একটু ডিফারেন্স আছে। ]

নিচে আমরা প্লেয়ার ক্লাস লিখছি, তারপর সেটা থেকে gamer অবজেক্ট বানাইছি। পরে যখন gamer.showDetails-কে কল করছি, তখন সেই মেথডের ভিতরে this বলতে gamer অবজেক্টকে বুঝায়।

javascript
class Player {
  constructor(name, level) {
    this.name = name;
    this.level = level;
  }

  showDetails() {
    console.log(this.name);
    console.log(this.level);
  }
}

const gamer = new Player("Alex", 5);
gamer.showDetails();

Output: Alex;
5;

4. Normal function

নরমাল একটা ফাংশনের মধ্যেও কিন্তু this ইউজ করা যায়। তবে সেটা ডাইরেক্ট window বা মেইন অবজেক্টকে বুঝাবে।

javascript
function doSomething(number) {
    console.log(this);
}
doSomething();

Output:
Window {0: global, window: Window, document: document, …}

5. Constructor Function-এর মধ্যে

মূলত ES6 আসার আগে class কি-ওয়ার্ড দিয়ে ক্লাস বানানো যেত না। তখন ফাংশন দিয়ে ক্লাস বানানো হতো। সেক্ষেত্রে ফাংশন একটা constructor হিসেবে কাজ করত। সেজন্য ফাংশনকে কনস্ট্রাক্টর হিসেবে ইউজ করলে ফাংশনের মধ্যে "this" নতুন তৈরি হওয়া অবজেক্টকেই নির্দেশ করে।

javascript
function Person(name) {
  this.name = name;
}

const user = new Person("Ayan");
console.log(user.name);

Output: Ayan;

এই Person ফাংশনকে কল করার সময় যদি তার নামের আগে new দিয়ে ফেলে, তাহলে সে নতুন একটা অবজেক্ট তৈরি করবে। সেক্ষেত্রে this বলতে গ্লোবাল বুঝাবে না; বরং বুঝাবে নতুন তৈরি হওয়া অবজেক্টকে। তাই this.name মানে ওই নতুন অবজেক্টের name প্রোপার্টিকে বুঝাবে।

যদিও ES6 আসার পর আর কেউ এইভাবে ক্লাস বানায় না। যদিও ES6 এসে আগের অনেক কিছুকে নতুনভাবে লেখার সিস্টেম নিয়ে আসছে, তাই কেউ কেউ ES6-এর ক্লাসকে syntactical sugar বলে। যদিও এইটা পুরাপুরি সত্যি না। কারণ, ES6-এর পরে ক্লাসের ভিতরে ভিতরেও কিছু চেইঞ্জ আসছে। বিশেষ করে ক্লাসের ওপরে এডভান্সড অনেক ফিচার আসছে, যেগুলা ES6-এর আগে করাই যেত না বা পুরাপুরি ডিফারেন্টভাবে করা হতো।

6. Event Listener-এর মধ্যে

ইভেন্ট হ্যান্ডলার বা লিসেনারে "this" সেই ইলিমেন্টকে নির্দেশ করে, যেটাতে ইভেন্ট ঘটছে。

javascript
<button id="get-user"> element that was clicked</button>;

document.getElementById("get-user").addEventListener("click", function () {
  console.log(this);
});

Output: <button id="get-user"> element that was clicked</button>;

এইখানে this বলতে সেই এলিমেন্ট বুঝায়। ওপরে আমরা যেহেতু একটা বাটনের ওপরে ক্লিক করেছি, তাই সে সেই বাটনকে this বানায় ফেলছে। যদি আমরা অন্য কিছুর উত্তরে ক্লিক বা অন্য কোনো ইভেন্ট ট্রিগার করতাম, তাহলে ইভেন্ট হ্যান্ডলারের ভিতরে this বলতে সেই এলিমেন্টকে বুঝাত।

7. Object মেথড arrow ফাংশন

অবজেক্টের মধ্যে মেথড ( ফাংশন) দুইভাবে ডিক্লেয়ার করা যায়। এক হচ্ছে নরমাল ফাংশন দিয়ে, আরেকটা হচ্ছে অ্যারো ফাংশন দিয়ে।

অবজেক্টের মধ্যে মেথড হিসেবে অ্যারো ফাংশন ডিক্লেয়ার করলে সেটার ভিতরে this আর নরমাল ফাংশনের ভিতরের this কিন্তু সেইম না। তাই অ্যারো ফাংশনে this দিয়ে যেই অবজেক্টের মধ্যে মেথড লিখছিস, সেটাকে বুঝাবে না।

অ্যারো ফাংশনে this হলো আউটার স্কোপের this। এখানে আউটার স্কোপ হলো গ্লোবাল স্কোপ (window বা global)। আর name প্রোপার্টি আছে person-এর মধ্যে, তার আউটার স্কোপ বা গ্লোবাল স্কোপে name প্রোপার্টি নেই, তাই this.name হলো undefined।

javascript
const person = {
   name: "John",
   greet: () => {
     return `Hello, I am ${this.name}`;
   }
};

console.log(person.greet());

Output: Hello, I am undefined

মেথড অ্যারো ফাংশন দিয়ে ডিক্লেয়ার করলে this এর ভ্যালু লেক্সিকাল স্কোপিং ফলো করে। এবং এইটা সেই অবজেক্ট কে বুঝে আউটার স্কোপকে বুঝায়।

8. "use strict" মোডে

যদিও স্ট্রিক্ট মোড নিয়ে এখনও আলোচনা করিনি। একটু পরে করব, তারপরেও জেনে রাখ— যদি "use strict" মোডে কাজ করিস, আর গ্লোবাল স্কোপে বা ফাংশনের বাইরে this ব্যবহার করিস, তাহলে এটা undefined হয়ে যাবে। কারণ, "strict mode"-এ this-কে window হিসেবে ধরে না।

javascript
"use strict";
function test() {
  console.log(this);
}
test();

Output: undefined;

ফাইনাল কথা হচ্ছে, এত কিছু দেখে তোর মাথা আউলায় যেতে পারে। কেন this এত এত আলাদা জিনিস বুঝায়।

কোথায় কল করতেছসthis-এর মান
Global ContextWindow বা global this
use strict-সহ গ্লোবাল কনটেক্সটundefined
Object-এর মেথডসেই অবজেক্ট
Constructor Functionনতুন তৈরি হওয়া অবজেক্ট
classনতুন তৈরি হওয়া অবজেক্ট
Event Listenerযে ইলিমেন্টে এ ইভেন্ট ঘটছে
Object মেথড Arrow functionWindow বা global this

this আলাদা আলাদা পরিস্থিতিতে আলাদা জিনিসকে নির্দেশ করলেও জিনিসটা আসলে ওতো কঠিন না; বরং পানির মতো সোজা। সেটা একটু পরে খুব সহজভাবে বলতেছি।

Practice:

  1. ব্রাউজারে window অবজেক্ট জিনিসটা কী?

  2. ব্রাউজারে console.log(this); রান করলে this-এর ভ্যালু কী হবে?

  3. অবজেক্টের মেথড নরমাল ফাংশন আর arrow ফাংশন দিয়ে ডিক্লেয়ার করলে this-এর কোনো কিছু কি ডিফারেন্ট হবে?

22-2: সহজ-সরল সিম্পল this

হুদাই পোলাপান this নিয়ে কনফিউজড হয়। এইটা আসলে সিম্পল। জাস্ট একটা উদাহরণ দিলে পানির মতো সোজা হয়ে যাবে।

নিচে একটা student নামে একটা অবজেক্ট আছে। সেটার মধ্যে একটা মেথড আছে। যেটার নাম getThis, আর এই মেথডকে কল করলে সে this-এর মান কনসোল লগ করবে। স্বাভাবিকভাবেই যদি student অবজেক্টের মধ্যে getThis-কে কল করি, তাহলে সে this বলতে student অবজেক্টকে কনসোল লগ করবে। আর এই মেথডকে কল করার জন্য student লিখে ডট চিহ্ন দিয়ে তারপর getThis-কে কল করতে হবে।

javascript
const student = {
    name: 'Learner',
    getThis() {
        console.log(this);
    }
}

student.getThis();

Output: {name: 'Learner', getThis: ƒ}

এইটুক পর্যন্ত বুঝলাম, কোনো মেথডের মধ্যে this বলতে সেই অবজেক্টকে বুঝায়। এইবার getThis মেথডকে ধার করে অন্য অবজেক্টে নিয়ে যাই। সেজন্য teacher নামে একটা অবজেক্ট বানালাম। সেই অবজেক্টে জাস্ট একটা name প্রোপার্টি। আর সেই অবজেক্টে getThis নামে একটা মেথড লিখলাম। আর সেই মেথডটা আসলে student অবজেক্টের getThis।

সেজন্য তুই দেখবি, নিচে সমান চিহ্ন দিয়ে এক অবজেক্টের মেথড অন্য অবজেক্টে নিয়ে আসছি। তারপর যদি teacher-এর পর ডট লিখে getThis কল করি, তাহলে সে কিন্তু teacher অবজেক্টকেই কনসোল লগ করবে।

javascript
const teacher = {
    name: 'Bambu Donor'
}

teacher.getThis = student.getThis;

teacher.getThis();

Output: {name: 'Bambu Donor', getThis: ƒ}

যদিও teacher অবজেক্টের getThis আসলে student অবজেক্টের getThis। তারপরেও teacher-এর পর ডট চিহ্ন দিয়ে সেটাকে কল করার কারণে সে কিন্তু this বলতে টিচারকেই বুঝাইছে।

এইটুক হালকা কনফিউজিং লাগতে পারে। আরেকটা উদাহরণ দেখ।

জাস্ট খুব সিম্পলভাবে freeThis নামে একটা ভেরিয়েবল ডিক্লেয়ার করলাম, যেটার মান হবে student অবজেক্টের getThis মেথড। এখন যদি freeThis নামক এই ফাংশন (যদিও সে স্টুডেন্ট অবজেক্টের getThis থেকে ধার করা) তারপরেও সে this বলতে সে window অবজেক্টকে বুঝাচ্ছে।

javascript
const freeThis = student.getThis;
freeThis();

this তুমি কার, কে তোমার?

এইবার ভালো করে খেয়াল কর। আমরা একই getThis মেথডকে তিন জায়গায় ইউজ করছি।

১. যখন student অবজেক্টের ওপরে কল করছি, তখন this বলতে সে student অবজেক্টকে বুঝাইছে।

২. যখন teacher অবজেক্টের ওপরে সেই একই getThis মেথডকে কল করছি, তখন this বলতে সে teacher অবজেক্টকে বুঝাইছে।

৩. আবার যখন কোনো কিছু ছাড়াই সেই একই getThis মেথডকে কল করছি, সেটা তখন window অবজেক্টকে বুঝায়।

এইটা থেকে বুঝা যায়। this বলতে কোথায় কীভাবে কল করা হচ্ছে, সেটার ওপরে নির্ভর করে। যদি কোনো অবজেক্টের ওপরে কল করা হয়, তাহলে সে অবজেক্টকে বুঝাবে। আর যদি কোনো কিছুর ওপরে কল করা না হয়, তাহলে সে গ্লোবাল অবজেক্ট বা window অবজেক্টকে বুঝাবে।

javascript
student.getThis();
teacher.getThis();

অর্থাৎ this বলতে বুঝায় ফাংশন বা মেথড কোন context-এ রান হচ্ছে।

আরও সিম্পলভাবে চিন্তা করলে this যে মেথড বা ফাংশনের ভিতরে লেখা আছে, তার আগে ডট চিহ্নের আগে কি কিছু লেখা আছে। যদি থাকে, তাহলে this বলতে সেটাকে বুঝাবে। আর যদি ডট না থাকে, তাহলে this বলতে window বা গ্লোবাল অবজেক্টকে বুঝাবে।

অর্থাৎ this যদি বুঝতে চাও, ডটের বামপাশে কী আছে, তার দিকে তাকাও।

Practice:

  1. ল্যাপটপ নামে একটা ভেরিয়েবল ডিক্লেয়ার কর। সেটার মধ্যে ব্র্যান্ড নামে একটা প্রোপার্টি থাকবে, যার মান হবে dell এবং getBrand নামে একটা মেথড থাকবে, যেটা ল্যাপটপের brand-কে কনসোল লগ করবে। এখন মোবাইল নামে একটা অবজেক্ট ডিক্লেয়ার কর। মোবাইলের মধ্যে ব্র্যান্ড থাকবে। তারপর ল্যাপটপ অবজেক্টের getBrand মেথডটি মোবাইলে এনে ইউজ কর।

  2. this কীভাবে নির্ধারিত হয়? কী কী context-এ এটা পরিবর্তন হয়?

  3. manager নামে একটি অবজেক্ট তৈরি কর যার মধ্যে assignWork নামে মেথড থাকবে। সেই assignWork মেথড এর ভিতরের manager এর tasks নামক প্রপার্টি (একটা অ্যারে) সেটাতে নতুন টাস্ক এর নাম যোগ করবে। এই কোড লিখে আউটপুট চেক কর। আর এই মেথড এর ভিতরে this বলতে কাকে বুঝায় সেটা মেথডের উপরে কমেন্ট করে লিখ।

22-3: this-কে bind দিয়া call কর

যখন ক্লাসের বেশির ভাগ পোলাপান পরীক্ষা বা ক্লাস টেস্ট পিছাতে চায়, তখন দেখা যায় ক্লাসের সবাই রাজি, শুধু একজন আঁতেল বেঁকে বসে। সে চায় পরীক্ষা অনটাইমেই হবে। তখন ক্লাসের সবাই তাকে ধরে বেঁধে পারলে কোলে তুলে স্যারের রুমে নিয়ে যায়— পরীক্ষা পেছানোর রিকোয়েস্ট করার জন্য। জাভাস্ক্রিপ্টে দরকার হলে this-কে ধরে বেঁধে নিয়ে আসা যায়।

জাভাস্ক্রিপ্টে this বলতে বুঝায় কোন কনটেক্সটে ফাংশন রান করবে বা মেথড রান করবে বা কোন জিনিসের ওপরে রান করবে।

একটা জিনিস আমরা সিম্পলভাবে জানি। অবজেক্টের ভিতরে কোনো মেথড বা ফাংশনের ভিতরে যদি this থাকে, তাহলে সেই this বলতে সেই ফাংশনকে বুঝায়।

এইবার সিম্পল একটা অবজেক্ট দেখি যেটার মধ্যে নাম, স্যালারি আর getInfo নামের মেথড আছে। getInfo মেথডের মধ্যে this অবজেক্ট থেকে নাম আর স্যালারি নিয়ে কনসোল লগ করতেছে। এখন আমি যদি getInfo মেথডকে কল করি, তাহলে সে স্বাভাবিকভাবেই tina অবজেক্ট থেকে ইনফো দেখাবে। নিচের মতো করে—

javascript
const tina = {
    name: "Tina",
    salary: 20000,
    getInfo() {
        console.log(`${this.name} salary: ${this.salary}`);
    }
};
tina.getInfo()

Output: Tina salary: 20000

এরপর আমার কাছে আরেকটা অবজেক্ট আছে, যেটার নাম soma।

javascript
const soma = {
  name: "Soma",
  salary: 15000,
};

ওপরে আরেকবার খেয়াল করে আয়, getInfo-এর মধ্যে কিন্তু আমরা this ইউজ করে this.name দিয়ে অবজেক্টের নাম নিচ্ছি। আবার একইভাবে this.salary দিয়ে অবজেক্টের স্যালারি নিচ্ছি। ভালো কথা।

এখন কাজ হচ্ছে, আমরা tina-এর getInfo মেথডটা soma-এর সাথে বেঁধে দিব।

.bind()

soma অবজেক্টের মধ্যে যদি tina-এর getInfo মেথড ইউজ করতে যাস, তাহলে this-এর মান হিসেবে tina-কে ইউজ না করে বরং soma-কে বাইন্ধা দিতে হবে। আর এই বাইন্দা দেয়ার কাজ করব আমরা bind দিয়ে। নিচের মতো করে একটা ফাংশন ডিক্লেয়ার কর। যেটার নাম দিবি somaInfo বা তুই চাইলে অন্য কোনো নামও দিতে পারস। আর ডানপাশে tina.getInfo লেখার পর ডট চিহ্ন দিয়ে bind লিখে ফেলবি।

তারপর bind-এর ভিতর প্রথম প্যারামিটার হিসেবে soma অবজেক্ট দিয়ে দিবি। তাহলে কী হবে? তাহলে এই getInfo ইউজ করার সময় সে this হিসেবে soma-কে যেহেতু বাইন্ধা দেয়া হইছে, তাই soma-কে this হিসেবে ইউজ করবে।

বিশ্বাস না হলে somaInfo ফাংশনকে কল করে দেখ।

javascript
const somaInfo = tina.getInfo.bind(soma);
somaInfo()

Output: Soma salary: 15000

কাহিনি বুঝছস। সেই একই getInfo মেথড এবং একই tina-এর মেথড কিন্তু তুই জোর করে soma-কে this হিসেবে বাইন্ধা দিতে পারছিস।

এইটা দিয়ে বুঝা যায়। তুই চাইলে তোর ইচ্ছামতো অবজেক্ট দিয়ে যেকোনো ফাংশন বা মেথডের this ভ্যালু সেট করতে পারবি।

অর্থাৎ .bind() হলো এমন একটা মেথড, যা ফাংশনকে permanently কোনো নির্দিষ্ট অবজেক্টের সাথে বেঁধে ফেলে। মানে, যখনই ফাংশনটা কল হবে, এটা ওই ফাংশনের context (মানে this)-এর মান জোর করে সেট করে দিবে।

.call

bind তো আমরা বাইন্ড করে একটা ফাংশনে রাখছি। তারপর কল করছি। এইটার ডাইরেক্ট ভার্সন হচ্ছে call ফাংশন। কল করার সময় মেথডের নামের পর ডট চিহ্ন দিয়ে তারপর call লিখে ব্র্যাকেটের মধ্যে কোনো একটা অবজেক্ট দিয়ে দিবি। তাহলে সেই অবজেক্টটাই হবে ঐ ফাংশনের ভিতরে this-এর মান।

নিচে mona নামে একটা অবজেক্ট আছে। তারপর সেই tina-এর getInfo লিখে ডট চিহ্ন দিয়ে call লিখে সেটার ভিতরে mona অবজেক্ট দিয়ে দিছি। তাহলে দেখ সে mona-এর ইনফো আউটপুট হিসেবে দিছে।

javascript
const mona = {
    name: 'Mona',
    salary: 25000
}

tina.getInfo.call(mona);

Output: Mona salary: 25000

দেখছিস, কী ম্যাজিক। tina-এর getInfo-এর ভিতরের this-এর মান কত সহজেই চেইঞ্জ করে দিসি।

.apply()

.apply() অনেকটা .call()-এর মতোই হালকা একটু ডিফারেন্স আছে। তবে বেসিক অংশ কিন্তু একই। জাস্ট ওপরের call-এর মতো করে apply করে দিবি। নিচে dina নাম একটা অবজেক্ট আছে। তার নিজস্ব কোনো getInfo মেথড নাই। তাই tina-এর getInfo method-এর ডট চিহ্ন দিয়ে apply লিখে ব্র্যাকেটের ভিতরে dina লিখে দিছি। যাতে tina-এর getInfo মেথড ইউজ করার সময় যত জায়গায় this ইউজ করা হইছে, সব জায়গায় this হিসেবে dina-কে ইউজ করে।

javascript
const dina = {
    name: 'Dina',
    salary: 35000
}

tina.getInfo.apply(dina);

Output: Dina salary: 35000

তুই দেখছস, কত সহজেই this-কে তুই পাল্টায় ফেলতে পারস।

bind call apply এর প্যারামিটার

ওপরে আমরা যখন getInfo মেথড লিখছিলাম, সেটার কোনো প্যারামিটার ছিল না। যদি প্যারামিটার থাকতো, তাহলে bind-এর ক্ষেত্রে প্রথম প্যারামিটার হিসেবে this-এর মান, তারপর বাকি প্যারামিটারগুলো কমা দিয়ে দিয়ে আলাদা করে দিতাম। সেইম সিস্টেমে call-এর ক্ষেত্রেও প্রথম প্যারামিটার হতো this, আর তারপরের প্যারামিটারগুলা কমা দিয়ে দিয়ে আলাদা করে দিতাম। অন্যদিকে apply-এর ক্ষেত্রে প্রথম প্যারামিটার this, আর বাকি প্যারামিটারগুলো একটা array-এর মধ্যে নিয়ে array হিসেবে পাস করা লাগে।

এই bind call apply-এর ডিফারেন্সের চাইতে ইম্পরট্যান্ট জিনিস হচ্ছে, তুই চাইলে যেকোনো ফাংশনের this-এর মান ইচ্ছামতো সেট করে দিতে পারস। সেটাই তোর একটা সুপার পাওয়ার।

Practice:

  1. টিম নামে একটা অবজেক্ট বানা, যার মধ্যে name এবং players থাকবে। players-এর মধ্যে খেলোয়াড়ের সংখ্যা থাকবে। এ ছাড়া টিমের মধ্যে একটা মেথড থাকবে play নামে, যেখানে this.name এবং this.players দিয়ে একটা স্ট্রিং বানিয়ে কনসোল লগ করবি। প্রথমে team থেকে play মেথডকে কল করে আউটপুট দেখ। তারপর আরেকটা অবজেক্ট বানা tournament নামে। সেটার মধ্যেও name এবং players থাকবে। টুর্নামেন্টে কতজন খেলোয়াড় খেলতেছে, সেই সংখ্যা থাকবে। এখন .bind() ইউজ করে team অবজেক্টের play মেথড ইউজ করবি, তবে সেটার this হিসেবে টুর্নামেন্টকে ইউজ করবি।

  2. একটা car অবজেক্ট বানা, যার মধ্যে speed এবং price থাকবে। এরপর একটি মেথড drive বানা, যেখানে this.speed এবং this.price দিয়ে গাড়ির তথ্য কনসোল লগ করবে। এখন car অবজেক্টের drive ইউজ করবি .call() ব্যবহার করে। তবে this-এর মান হিসেবে একবার bike নামের একটা অবজেক্ট দিয়ে দিবি। আবার truck নামের আরেকটা অবজেক্ট দিয়ে দিবি। আর bike এবং truck এই দুই অবজেক্টই speed আর price প্রোপার্টি থাকবে।

  3. একটা অবজেক্ট বানা "employee" নামে, যার মধ্যে name এবং role থাকবে। employee-এর মধ্যে একটা মেথড থাকবে "details" নামে, যেখানে this.name এবং this.role দিয়ে কনসোল লগ করবে। প্রথমে employee থেকে details মেথড কল করে আউটপুট দেখ। এরপর আরেকটা অবজেক্ট বানা "manager" নামে, যার মধ্যে name এবং role থাকবে। এখন .apply() ব্যবহার করে employee-এর details মেথড manager ইউজ কর।

  4. একটা অবজেক্ট বানা "classroom" নামে, যার মধ্যে name এবং students থাকবে। students-এর মধ্যে অনেকগুলা student-এর নাম থাকবে। classroom-এর মধ্যে একটা মেথড থাকবে "attendance" নামে, যেখানে this.name এবং this.students-এর নামগুলো কমা দিয়ে join করে স্ট্রিং বানিয়ে কনসোল লগ করবে। প্রথমে classroom থেকে attendance মেথড কল করে আউটপুট দেখ। এরপর অন্য একটা অবজেক্ট বানা "lab" নামে, যার মধ্যে name এবং students থাকবে। এবার .bind() ব্যবহার করে classroom-এর attendance মেথড lab-এর সাথে বেঁধে দে।

22-4: ক্লোজ আপ-এর Closure

ধর, তোর কাল্পনিক বিয়েতে তোর কিপ্টা শ্বশুর বলছে, একটা রোস্টের বেশি খাওয়ামু না। তাই সে বাবুর্চির সাথে বসছে। আর খাবার সার্ভ করার জন্য পাঁচ-ছয়জনকে নিয়ে আসছে। কুদ্দুস, মফিজ, শামছু, মোখলেছ, আক্কাস, সোলায়মান। সবাইকে শক্তভাবে বলে দিছে— একদম পই পই করে শকুনের মতো হিসাব রাখবি। সবাই আলাদাভাবে যার যার মতো করে মনে মনে হিসাব রাখবি— কে কয়টা রোস্ট দিছস। একজনের হিসাবের সাথে আরেকজন মিলিয়ে ফেলবি না। তাইলে তোদের সব কয়টাকে আমি একটা রোস্টও খাইতে দিমু না।

এখন তারা যদি পাঁচ-ছয়টা ভেরিয়েবল ডিক্লেয়ার করে ফেলে—

javascript
let kuddusRoast = 0;
let mofizRoast = 0;
let samsuRoast = 0;
let mokhlesRoast = 0;
let akkasRoast = 0;
let solaimanRoast = 0;

একজন আরেকজনের ভেরিয়েবল কিন্তু চেইঞ্জ করে ফেলতে পারে বা কার ভেরিয়েবলের মান কত, আরেকজন জেনে যাবে। যার যার মতো গোপনে (মনে মনে) হিসাব করা হবে না।

উপায় না খুঁজে পেয়ে তোর কিপ্টা শ্বশুর আসলো তোর কাছে। তুই বললি— ঠিক আছে শ্বশুর আব্বা, আমি নিজের বিয়ের কাজ বন্ধ করে আপাতত গোপনে হিসাব রাখার এবং কার মনের ভিতরে কী আছে, সব দরজা-জানালা সব বন্ধ করে দিব, ক্লোজ করে দিব। একদম সানডে-মানডে ক্লোজ করে দিয়ে ক্লোজার বানিয়ে দিব।

১. সবাইরে একটা করে ফাংশন দিব। এই ফাংশনকে যে যে যার যার মতো করে কল করে, তার জন্য হিসাব রাখার একটা ফাংশন পাবে।

২. ফাংশন একটা হলেও এইটাকে যখন যে কল করবে, তখন তার জন্য সেই ফাংশন থেকে এটিকে ফাংশন রিটার্ন করে দিবে।

৩. প্রতিটা ফাংশনকে কল করলে সেটার জন্য আলাদা আলাদা এক্সিকিউশন কনটেক্সট হবে এবং ফাংশনের ভিতরে যত ভেরিয়েবল আছে, সেগুলার একটা স্কোপ থাকবে।

৪. ফাংশনের ভিতরে যত ভেরিয়েবল আছে, সেগুলা ফাংশনের ভিতরে যত দরকার ইউজ করা যায়, চেইঞ্জ করা যায়। তবে ফাংশনের বাইর থেকে এইগুলাকে এক্সেস করা যায় না, ইউজ করা যায় না। চাইলে বাহির থেকে চেইঞ্জও করা যায় না।

এখন একটা ফাংশন লিখে ফেল, যেটার নাম counter; আর এই ফাংশনের মধ্যে count নামে একটা ভেরিয়েবল আছে। তারপরের লাইনে count++ দিয়ে কাউন্ট ভেরিয়েবলের মান এক বাড়ানো হচ্ছে। তারপরের লাইনে count-কে রিটার্ন করে দিচ্ছে। খুবই সিম্পল একটা ফাংশন।

javascript
function counter() {
  let count = 0;
  count++;
  return count;
}

ওপরের ফাংশনের count ভেরিয়েবলকে কিন্তু ফাংশনের ভিতরে ইউজ করা যাচ্ছে। যতজন এই ফাংশনকে কল করবে, সবার জন্য আলাদা আলাদা count ভেরিয়েবল তৈরি হবে। সেটা ফাংশনের ভিতরে চেইঞ্জও হবে। তবে ফাংশনের বাহির থেকে কেউ count ভেরিয়েবল এক্সেস করতে পারবে না। বাহির থেকে চেইঞ্জ করা তো দূরেরই কথা। অর্থাৎ ফাংশনের ভিতরে count ভেরিয়েবলের একটা ছোট জগৎ বা স্কোপ তৈরি হইছে। সে শুধু এই জগতের মধ্যেই থাকবে।

এইটা দিয়ে একটা সুবিধা হচ্ছে— এইটাকে ফাংশনের ভিতরের ভেরিয়েবল বাইর থেকে চেইঞ্জ বা মোডিফাই করার কোনো সিস্টেম নাই।

৫. আমরা ফাংশনের ভিতর থেকে চাইলে ভেরিয়েবল, স্ট্রিং, নাম্বার, অ্যারে এইগুলা রিটার্ন করতে পারি। যেটা তুই আগে থেকেই জানস। একইভাবে আমরা চাইলে ফাংশনের ভিতর থেকে আরেকটা ফ্যাংশকেও রিটার্ন করতে পারবি।

javascript
function counter() {
  let count = 0;
  count++;

  return function innerFunction() {};
}

যে ফাংশনকে রিটার্ন করবি, সেটার নাম থাকতে পারে, আবার নাম নাও থাকতে পারে।

javascript
function counter() {
  let count = 0;
  count++;

  return function () {};
}

তবে ফাংশনের ভিতরে যেহেতু আরেকটা ফাংশন আছে। সে কিন্তু ওপরের যত ভেরিয়েবল বা প্যারামিটার আছে, সবগুলাকে এক্সেস করতে পারবে। কারণ, সহজ করে বললে— ফাংশনের ভিতরে যা যা আছে, তার সবই ফাংশনের মধ্যে ইউজ করা যায়। যদিও কিছু কিন্তু আছে। তবে সহজভাবে বললে বলা যায়, সবই এক্সেস করতে পারবে।

অর্থাৎ যে ফাংশনটাকে রিটার্ন করা হবে, সেটার ভিতর থেকেও চাইলে count ভেরিয়েবলকে এক্সেস করা যাবে এবং চাইলে সেটার মান বাড়ানোও যাবে বা মোডিফাই করা যাবে।

৬. এই সুযোগটাই আমরা নিব। তাই যে ফাংশনটাকে রিটার্ন করা হচ্ছে, তার ওপরে যে count++ আছে, সেটাকে সেখান থেকে সরিয়ে সে রিটার্ন ফাংশনের ভিতরে নিয়ে আসব।

javascript
function counter() {
  let count = 0;

  return function () {
    count++;
  };
}

এতে চমৎকার একটা কাজ কিন্তু হয়ে গেছে। কেউ counter ফাংশনকে কল করলে সে রিটার্ন হিসেবে কোন স্ট্রিং, সংখ্যা বা অ্যারে পাবে না, সে পাবে আরেকটা ফাংশন। এই রিটার্ন ফাংশনকে সে চাইলে একটা ভেরিয়েবলে রাখতেই পারে। নিচের মতো করে—

javascript
const kuddusRoast = counter();

যেহেতু counter ফাংশন থেকে একটা আরেকটা ফাংশনকে রিটার্ন করছে, তাই kuddusRoast হবে একটা ফাংশন। আর kuddusRoast যেহেতু একটা ফাংশন, এইটাকে কিন্তু ফাংশনের মতো করে কল করতে পারবি।

javascript
kuddusRoast();

আর kuddusRoast-কে কল করা মানে counter ফাংশন থেকে যেটাকে রিটার্ন করা হইছে সেটাকে কল করা। অর্থাৎ— নিচের ফাংশনকে কল করা যায়।

javascript
  function() {
      count++;
  };

এখন kuddusRoast-কে কল করলে সে counter ফাংশনের ভিতরে যে ফাংশনটাকে রিটার্ন করা হইছে, সেটার ভিতরে গিয়ে counter ফাংশনের ভিতরের count++ থাকায় count ভেরিয়েবলের মান এক বাড়িয়ে ফেলবে।

তবে মজার বিষয় হচ্ছে— এই count ভেরিয়েবল যেহেতু counter ফাংশনের ভিতরে, তাই এইটাকে বাহির থেকে কেউ এক্সেস বা মোডিফাই করতে পারবে না। একটা ক্লোজ একটা জায়গায় আছে। তবে kuddusRoast-কে কল করে count-এর মান বাড়াতে পারবে।

৭. আমরা চাইলে counter ফাংশন থেকে যে ফাংশনকে রিটার্ন করা হয়েছে (সেটার নাম থাকুক বা না থাকুক), সেটা থেকেও কিন্তু আমরা কোনো একটা ভেরিয়েবল, array বা অন্য কিছু রিটার্ন করতে পারি। তাই আমরা সেটার ভিতর থেকে count ভেরিয়েবলকেই রিটার্ন করে ফেললাম।

javascript
function counter() {
  let count = 0;

  return function () {
    count++;
    return count;
  };
}

৮. এখন হবে আসল খেলা। ওপরের counter ফাংশনকে আমরা একেকজনের জন্য আলাদা আলাদাভাবে কল করতে পারি।

javascript
const kuddusRoast = counter();
const mofizRoast = counter();
const mokhlesRoast = counter();
const akkasRoast = counter();
const solaimanRoast = counter();

তাহলে কয়েকটা চমৎকার জিনিস হবে। প্রত্যেকের জন্য আলাদা আলাদা করে একটা counter ফাংশনের ইন্সটেন্স তৈরি হবে এবং সবার জন্য আলাদা আলাদা একটা count ভেরিয়েবল থাকবে এবং এইসব count ভেরিয়েবল যেহেতু counter ফাংশনের ভিতরে, তাই বাহির থেকে কেউ এক্সেস করতে পারবে না। তবে counter ফাংশন থেকে যেহেতু একটা ফাংশন রিটার্ন দেয়া হয়েছে, তাই সেই সব রিটার্ন ফাংশন চাইলে যার যার জন্য আলাদা count ভেরিয়েবলকে এক্সেস বা মডিফাই করতে পারবে।

শুধু রিটার্ন করা ফাংশনকে কল করে (সেটা যে যে নামেই কল করুক না কেন: kuddusRoast, mofizRoast, mokhlesRoast, akkasRoast, solaimanRoast), তাদের আলাদা আলাদা count ভেরিয়েবল থাকবে এবং যার যার আলাদা আলাদাভাবে count ভেরিয়েবলের মান চেইঞ্জ হবে।

অর্থাৎ একজনের হিসাব আরেকজন জানবে না। মোডিফাই করতে পারবে না। সে তার তার মতো করে ক্লোজ একটা হিসাব রাখতে পারবে। আর এই জিনিসটাকেই জাভাস্ক্রিপ্ট জগতে বলে ক্লোজার (closure)।

আরো গুছিয়ে বললে— একটা ফাংশন রান করে ফেলার পরেও সেই ফাংশনের বাইরের স্কোপের (বন্ধ স্কোপের) ভেরিয়েবলকে এক্সেস করার সামর্থ্যকেই closure বলে।

এইবার একটা ক্লোজার শিখে একটা ক্লোজআপ মার্কা হাসি দে।

Practice:

  1. closure কী জিনিস?
  2. তুই একটা fridgeTracker() ফাংশন বানা এবং তোর মেসের সবার জন্য আলাদা আলাদা ক্লোজার বানা। যাতে যে যতবার ফ্রিজ ওপেন করবে, তার কাউন্টার তত বাড়বে।
  3. তুই একটা taskTracker() ফাংশন বানা, যেখানে প্রতিবার একটা কাজ ফিনিশ হওয়ার পর কাউন্ট এক এক করে বাড়বে। তোর বা তোর ফ্রেন্ড সবার কাজের স্কোপ আলাদা আলাদা থাকবে।

22-5: Hoisting-এর মরণ কামড়

বড় বড় বিল্ডিং বানানোর সময় বিশাল বিশাল ক্রেনে করে ইট, সিমেন্ট ও বালু ওপরে উঠানো হয় কাজ করার জন্য। একইভাবে JavaScript-এ কাজের সুবিধার জন্য ভেরিয়েবল ও ফাংশনের নাম (ডিক্লারেশন) ওপরে তোলা হয়। যেন পুরো কোডে এগুলো যেখানে যেখানে দরকার, ইউজ করতে পারে।

নিচে sayHello নাম একটা ফাংশনটি ডিক্লেয়ার করছি। কিন্তু সেটা ডিক্লেয়ার করার আগেই কল করে ফেলছি, তবুও এটি কাজ করবে। কারণ, JavaScript প্রথমেই ফাংশনের ডিক্লারেশনকে ওপরে তুলে নিয়ে যায়। যাতে ডিক্লেয়ার করার আগেই যদি কখনো দরকার হয়, তাহলে ফাংশনটি ইউজ করা যায়। আর ডিক্লেয়ার করার পরে তো এমনি এমনি কাজ করবেই।

javascript
  sayHello();

  function sayHello() {
      console.log("Hello, world!");
  }

Output: Hello, world!

ফাংশন ডিক্লারেশনকে ভিতরে ভিতরে ওপরে তুলে নেয়ার এই প্রসেসকে hoisting বলে। এই hoisting একটা কঠিন শব্দ। এইটার মানে হচ্ছে উত্তোলন করা, ওপরে উঠানো।

এইটুক পর্যন্ত hoisting ইজি আর সিম্পল আছে।

তবে let বা const দিয়ে ভেরিয়েবল ডিক্লেয়ার করলে তলে তলে সেগুলাকে hoisting করা হলেও ভ্যালু সেট করা হয় না (এইটা শুনতে বা চিন্তা করতে একটু ভেজাইল্লা লাগে)। তবে সিম্পলভাবে চিন্তা করতে পারস, let বা const দিয়ে ভেরিয়েবল ডিক্লেয়ার করলে সেটা ডিক্লেয়ারের আগে ইউজ করা যাবে না। এরর দিবে।

let আর const দিয়ে ডিক্লেয়ার করা ভেরিয়েবলগুলো hoist করে একটা জায়গায় রাখে। এই জায়গার নাম Temporal Dead Zone (TDZ)। মানে সাময়িক মৃত্যুর কূপ বা মরণের এলাকা। এই জোনে থাকলে ভেরিয়েবল ইউজ করা যাবে না, পাবেও না; বরং ইউজ করতে চাইলে ReferenceError দিবে। তবে যেখানে let বা const-কে ডিক্লেয়ার করা আছে, তার লাইনে গেলে তখন সে ভেরিয়েবলগুলাকে ইনিশিয়ালাইজ করে ফেলে। অর্থাৎ তাদের মান সেট করে ফেলে।

সেজন্য let আর const দিয়ে ডিক্লেয়ার করা ভেরিয়েবল যেখানে ডিক্লেয়ার করা হইছে, তার আগে ইউজ করা যায় না ( যদিও ভিতরে ভিতরে hoisting ঠিকই হয়)। তবে ডিক্লেয়ার করার পরে ঠিকই ইউজ করা যায়।

javascript
  console.log(myLet);
  console.log(myConst);
  let myLet = 20;
  const myConst = 30;

Output:
ReferenceError: Cannot access 'myLet' before initialization
ReferenceError: Cannot access 'myConst' before initialization

শুধু নরমাল ভেরিয়েবল না। তুই যদি ফাংশন এক্সপ্রেশন দিয়ে একটা ফাংশন লিখছ, সেটার বামপাশে কিন্তু একটা ভেরিয়েবলই। তাই এইটাও let আর const-এর মতো সেইম কেইস। তলে তলে hoisting কাজ করলেও মান সেট হয় না। তাই যেখানে কোড লেখা হয়েছে, তার ওপরে কাজ করবে না। এরর দিবে।

javascript
  sayHello();

  const sayHello = function() {
      console.log("Hello!");
  };

Output: Uncaught ReferenceError: Cannot access 'sayHello' before initialization

ফাংশন এক্সপ্রেশনের মতো করে অ্যারো ফাংশনও সেইম কেইস। এইটাও সিম্পল let আর const-এর মতো। সেইম কারণে ডিক্লেয়ারের আগে সেটাকে কল করে ফেললে এরর দিবে।

javascript
  console.log(greet);

  const greet = () => {
      console.log("Hello!");
  };

Output: Uncaught ReferenceError: Cannot access 'greet' before initialization

পুরান আমলের var দিয়ে ভেরিয়েবল ডিক্লেয়ার করলে আবার আরেকটু ডিফারেন্ট কাহিনি। var-দিয়ে ডিক্লেয়ার করা ভেরিয়েবলগুলার নামটা hoist করে কিন্তু মানটা নেয় না। অর্থাৎ var দিয়ে ডিক্লেয়ার করা ভেরিয়েবলকে hositing করে ইনিশিয়ালাইজ করে ফেলে। কিন্তু কোনো মান সেট করে না। আর কোনো মান সেট না করার কারণে ডিফল্ট মান হিসেবে undefined হয়ে যায়।

সেজন্য var দিয়ে ডিক্লেয়ার করা ভেরিয়েবলগুলার মান সেট হওয়ার আগ পর্যন্ত ইউজ করা যাবে। তবে মান পাওয়া যাবে না। মান হিসেবে undefined পাওয়া যাবে। আর ডিক্লেয়ার করার পর ঠিকই মান পাওয়া যাবে।

javascript
console.log(myVar);
var myVar = 10;
console.log(myVar);

Output: undefined;
10;

Hoisting-এ একটা ভেজাইল্লা টপিক। এইটার ভিতরে আরও অনেক অনেক জিনিস আছে। আমি জাস্ট হালকার ওপরে ঝাপসা করে কয়েকটা জিনিস বলেছি।

Practice:

  1. একটা ভেরিয়েবল ডিক্লেয়ার কর, যেটার মধ্যে কমেন্টের সংখ্যা থাকবে। এখন এই ভেরিয়েবলটাকে ডিক্লেয়ার করার আগের লাইনে এইটার মান এক বাড়ানোর চেষ্টা কর। দেখ কী হয়।
  2. const দিয়ে views নামে একটা ভেরিয়েবল ডিক্লেয়ার কর। আর ডিক্লেয়ার করার আগের লাইনে এই ভেরিয়েবলকে 2 দিয়ে ভাগ কর। তাহলে ভাগফল কত পাবি?
  3. Temporal Dead Zone (TDZ) কাকে বলে এবং এটি কেন গুরুত্বপূর্ণ?
  4. function expression দিয়ে লেখা ফাংশনকে hoisting করলে কেন initialization-এর আগে কল করা যায় না?
  5. var দিয়ে ডিক্লেয়ার করা ভেরিয়েবলের hoisting behavior কেমন? উদাহরণসহ ব্যাখ্যা কর।
  6. let এবং var-এর hoisting behavior একই?
  7. দুইটা সংখ্যার মধ্যে বড় সংখ্যা কোনটা, সেটা বের করার একটা ফাংশন লিখে ফেল। তারপর এই ফাংশন ডিক্লেয়ার করার আগেই সেই ফাংশনকে কল করে ফেল 87 আর 12 দিয়ে। তারপর রিটার্ন যেটা পাবি, সেটাকে কনসোল লগ করে ফেল। যেখানে কনসোল লগ করছস, সেখানে কমেন্ট করে লিখে ফেল, কীভাবে ফাংশন ডিক্লেয়ার করার আগেই সেই ফাংশনকে ইউজ করতে পারলি।

22-6: Execution context-এর গোমর ফাঁস

তুই যখন একটা ফাংশনকে কল করিস, তোর মনে হতে পারে, জাস্ট কল করলাম আর আউটপুট দিয়ে দিলো। তবে বিষয়টা ওতো সিম্পল হলেও তলে তলে অনেক কিছু ঘটতে থাকে, যা তোর খালি চোখে পড়ে না।

Global Context

কোড শুরু হওয়ার একদম শুরুতে জাভাস্ক্রিপ্ট global execution context তৈরি করে। যেটার মধ্যে সব গ্লোবাল ভেরিয়েবল, সব গ্লোবাল ফাংশন এবং জাভাস্ক্রিপ্টের বিল্ট-ইন ফাংশন, যেমন setTimeout, console.log এগুলো গ্লোবাল স্কোপে রেডি করে রাখে।

এই গ্লোবাল কনটেক্সট হলো জাভাস্ক্রিপ্ট প্রোগ্রামের সবচেয়ে বড় এবং মেইন স্কোপ। এখানে ডিক্লেয়ার করা যেকোনো ভেরিয়েবল, ফাংশন অবজেক্ট গ্লোবাল স্কোপে থাকলে সেটাকে যেকোনো জায়গা থেকে এক্সেস করা যায়। গ্লোবাল কনটেক্সটের নাম ডিপেন্ড করে জাভাস্ক্রিপ্ট কোড কোথায় রান হচ্ছে তার ওপর। ব্রাউজারে জাভাস্ক্রিপ্ট রান করলে এই গ্লোবাল কনটেক্সটের নাম: window, Node.js-এ: global, আর ES2020-এর পর সব এনভায়রনমেন্ট চলে আসছে globalThis।

Function Declare

কোড চলতে চলতে যখন একটা ফাংশন পায়, তখন জাভাস্ক্রিপ্ট ওই ফাংশনের reference তৈরি করে। কিন্তু ফাংশন execute করে না বা কল করে না। ভিতরে কী হবে, সেটাও চেক করে না। কারণ, ফাংশনকে কে কীভাবে কল করবে বা কোন সিচুয়েশনে কল করবে, সেটা অনুসারে ভিতরের জিনিস চেইঞ্জ হতে পারে। অর্থাৎ context চেইঞ্জ হতে পারে। তাই ফাংশন কল না করা পর্যন্ত ফাংশনের ভিতরের কিছুই ঘটবে না।

ফাংশনের reference তৈরি করা মানে হলো ওই ফাংশন কোথায় আছে, সেটা মেমোরিতে উল্লেখ করে রাখে।

Function Execution Context

কোনো একটা ফাংশনকে কল করলে একদম ফার্স্ট কাজ হচ্ছে এই ফাংশনের জন্য নতুন একটা Execution Context তৈরি করা। এই Execution Context হলো কোন কনটেক্সটে ফাংশন রান হবে, সেটা ঠিক করা।

তখন ঠিক করে, এই ফাংশনের কী কী আছে, কী কী করতে পারবে, কয়টা প্যারামিটার আছে। ফাংশন কী কী ভেরিয়েবল এক্সেস করতে পারবে। তার স্কোপ, স্কোপ চেইন— সব ঠিক করার কাজ কল করার পর শুরু হয়।

Execution Context তৈরি হয় দুইটা ধাপে:

Creation Phase: একটা Execution Context তৈরি হলে ছোট করে বললে কয়েকটা কাজ হয়।

  • যে যে আর্গুমেন্ট পাস করছে, সেই অনুসারে ফাংশনের প্যারামিটারগুলোর মান সেট করে।
  • যা যা hoisting করা দরকার, সব hoisting করে ফেলে।
  • let এবং const দিয়ে ডিক্লেয়ার করা ভেরিয়েবলগুলোর জন্য মেমোরিতে জায়গা ঠিক করে রাখে। তবে সেগুলাকে ইনিশিয়ালাইজ করে না; বরং সেগুলাকে টেম্পোরারি ডেড জোনে রেখে দেয়। যাতে এগুলা ডিক্লেয়ার করার আগেই অ্যাক্সেস করার চেষ্টা করলে Reference Error দিতে পারে।
  • var দিয়ে ডিক্লেয়ার করা ভেরিয়েবলগুলাকে undefined মান দিয়ে ওপরে উঠায় নেয় তাদের ইনিশিয়ালাইজ করে।
  • কোডের স্কোপ আর স্কোপ চেইন তৈরি করে। যাতে আউটার স্কোপ বা গ্লোবাল স্কোপের ভেরিয়েবল এক্সেস করতে পারে।
  • ফাংশন কল করার ধরন অনুসারে ঠিক করে, this-এর মান কী হবে।

Execution Phase: এখন কোডটা এক্সিকিউট করা শুরু হবে। তখন প্রতিটা ভ্যারিয়েবল তার আসল ভ্যালু পাবে, ফাংশন চলবে, আর সব ক্যালকুলেশন বা লজিক এক্সিকিউট হবে। আউটপুট দিবে বা রিটার্ন করবে।

Practice:

  1. Execution Context কী জিনিস? এইটা কীভাবে কাজ করে?
  2. গ্লোবাল কনটেক্সটের এর কাজ কী? এইটা কেন দরকার পড়ে?

Released under the MIT License.