ক্লোজ আপ-এর Closure
ধর, তোর কাল্পনিক বিয়েতে তোর কিপ্টা শ্বশুর বলছে, একটা রোস্টের বেশি খাওয়ামু না। তাই সে বাবুর্চির সাথে বসছে। আর খাবার সার্ভ করার জন্য পাঁচ-ছয়জনকে নিয়ে আসছে। কুদ্দুস, মফিজ, শামছু, মোখলেছ, আক্কাস, সোলায়মান। সবাইকে শক্তভাবে বলে দিছে— একদম পই পই করে শকুনের মতো হিসাব রাখবি। সবাই আলাদাভাবে যার যার মতো করে মনে মনে হিসাব রাখবি— কে কয়টা রোস্ট দিছস। একজনের হিসাবের সাথে আরেকজন মিলিয়ে ফেলবি না। তাইলে তোদের সব কয়টাকে আমি একটা রোস্টও খাইতে দিমু না।
এখন তারা যদি পাঁচ-ছয়টা ভেরিয়েবল ডিক্লেয়ার করে ফেলে–
let kuddusRoast = 0;
let mofizRoast = 0;
let samsuRoast = 0;
let mokhlesRoast = 0;
let akkasRoast = 0;
let solaimanRoast = 0;একজন আরেকজনের ভেরিয়েবল কিন্তু চেইঞ্জ করে ফেলতে পারে বা কার ভেরিয়েবলের মান কত, আরেকজন জেনে যাবে। যার যার মতো গোপনে (মনে মনে) হিসাব করা হবে না।
উপায় না খুঁজে পেয়ে তোর কিপ্টা শ্বশুর আসলো তোর কাছে। তুই বললি— ঠিক আছে শ্বশুর আব্বা, আমি নিজের বিয়ের কাজ বন্ধ করে আপাতত গোপনে হিসাব রাখার এবং কার মনের ভিতরে কী আছে, সব দরজা-জানালা সব বন্ধ করে দিব, ক্লোজ করে দিব। একদম সানডে-মানডে ক্লোজ করে দিয়ে ক্লোজার বানিয়ে দিব।
১. সবাইরে একটা করে ফাংশন দিব। এই ফাংশনকে যে যে যার যার মতো করে কল করে, তার জন্য হিসাব রাখার একটা ফাংশন পাবে।
২. ফাংশন একটা হলেও এইটাকে যখন যে কল করবে, তখন তার জন্য সেই ফাংশন থেকে এটিকে ফাংশন রিটার্ন করে দিবে।
৩. প্রতিটা ফাংশনকে কল করলে সেটার জন্য আলাদা আলাদা এক্সিকিউশন কনটেক্সট হবে এবং ফাংশনের ভিতরে যত ভেরিয়েবল আছে, সেগুলার একটা স্কোপ থাকবে।
৪. ফাংশনের ভিতরে যত ভেরিয়েবল আছে, সেগুলা ফাংশনের ভিতরে যত দরকার ইউজ করা যায়, চেইঞ্জ করা যায়। তবে ফাংশনের বাইর থেকে এইগুলাকে এক্সেস করা যায় না, ইউজ করা যায় না। চাইলে বাহির থেকে চেইঞ্জও করা যায় না।
এখন একটা ফাংশন লিখে ফেল, যেটার নাম counter; আর এই ফাংশনের মধ্যে count নামে একটা ভেরিয়েবল আছে। তারপরের লাইনে count++ দিয়ে কাউন্ট ভেরিয়েবলের মান এক বাড়ানো হচ্ছে। তারপরের লাইনে count-কে রিটার্ন করে দিচ্ছে। খুবই সিম্পল একটা ফাংশন।
function counter(){
let count = 0;
count++;
return count;
}ওপরের ফাংশনের count ভেরিয়েবলকে কিন্তু ফাংশনের ভিতরে ইউজ করা যাচ্ছে। যতজন এই ফাংশনকে কল করবে, সবার জন্য আলাদা আলাদা count ভেরিয়েবল তৈরি হবে। সেটা ফাংশনের ভিতরে চেইঞ্জও হবে। তবে ফাংশনের বাহির থেকে কেউ count ভেরিয়েবল এক্সেস করতে পারবে না। বাহির থেকে চেইঞ্জ করা তো দূরেরই কথা। অর্থাৎ ফাংশনের ভিতরে count ভেরিয়েবলের একটা ছোট জগৎ বা স্কোপ তৈরি হইছে। সে শুধু এই জগতের মধ্যেই থাকবে।
এইটা দিয়ে একটা সুবিধা হচ্ছে— এইটাকে ফাংশনের ভিতরের ভেরিয়েবল বাইর থেকে চেইঞ্জ বা মোডিফাই করার কোনো সিস্টেম নাই।
৫. আমরা ফাংশনের ভিতর থেকে চাইলে ভেরিয়েবল, স্ট্রিং, নাম্বার, অ্যারে এইগুলা রিটার্ন করতে পারি। যেটা তুই আগে থেকেই জানস। একইভাবে আমরা চাইলে ফাংশনের ভিতর থেকে আরেকটা ফ্যাংশকেও রিটার্ন করতে পারবি।
function counter(){
let count = 0;
count++;
return function innerFunction(){
};
}যে ফাংশনকে রিটার্ন করবি, সেটার নাম থাকতে পারে, আবার নাম নাও থাকতে পারে।
function counter(){
let count = 0;
count++;
return function(){
};
}তবে ফাংশনের ভিতরে যেহেতু আরেকটা ফাংশন আছে। সে কিন্তু ওপরের যত ভেরিয়েবল বা প্যারামিটার আছে, সবগুলাকে এক্সেস করতে পারবে। কারণ, সহজ করে বললে— ফাংশনের ভিতরে যা যা আছে, তার সবই ফাংশনের মধ্যে ইউজ করা যায়। যদিও কিছু কিন্তু আছে। তবে সহজভাবে বললে বলা যায়, সবই এক্সেস করতে পারবে।
অর্থাৎ যে ফাংশনটাকে রিটার্ন করা হবে, সেটার ভিতর থেকেও চাইলে count ভেরিয়েবলকে এক্সেস করা যাবে এবং চাইলে সেটার মান বাড়ানোও যাবে বা মোডিফাই করা যাবে।
৬. এই সুযোগটাই আমরা নিব। তাই যে ফাংশনটাকে রিটার্ন করা হচ্ছে, তার ওপরে যে count++ আছে, সেটাকে সেখান থেকে সরিয়ে সে রিটার্ন ফাংশনের ভিতরে নিয়ে আসব।
function counter(){
let count = 0;
return function(){
count++;
};
}এতে চমৎকার একটা কাজ কিন্তু হয়ে গেছে। কেউ counter ফাংশনকে কল করলে সে রিটার্ন হিসেবে কোন স্ট্রিং, সংখ্যা বা অ্যারে পাবে না, সে পাবে আরেকটা ফাংশন। এই রিটার্ন ফাংশনকে সে চাইলে একটা ভেরিয়েবলে রাখতেই পারে। নিচের মতো করে—
const kuddusRoast = counter();যেহেতু counter ফাংশন থেকে একটা আরেকটা ফাংশনকে রিটার্ন করছে, তাই kuddusRoast হবে একটা ফাংশন। আর kuddusRoast যেহেতু একটা ফাংশন, এইটাকে কিন্তু ফাংশনের মতো করে কল করতে পারবি।
kuddusRoast()আর kuddusRoast-কে কল করা মানে counter ফাংশন থেকে যেটাকে রিটার্ন করা হইছে সেটাকে কল করা। অর্থাৎ— নিচের ফাংশনকে কল করা যায়।
function(){
count++;
};এখন kuddusRoast-কে কল করলে সে counter ফাংশনের ভিতরে যে ফাংশনটাকে রিটার্ন করা হইছে, সেটার ভিতরে গিয়ে counter ফাংশনের ভিতরের count++ থাকায় count ভেরিয়েবলের মান এক বাড়িয়ে ফেলবে।
তবে মজার বিষয় হচ্ছে— এই count ভেরিয়েবল যেহেতু counter ফাংশনের ভিতরে, তাই এইটাকে বাহির থেকে কেউ এক্সেস বা মোডিফাই করতে পারবে না। একটা ক্লোজ একটা জায়গায় আছে। তবে kuddusRoast-কে কল করে count-এর মান বাড়াতে পারবে।
৭. আমরা চাইলে counter ফাংশন থেকে যে ফাংশনকে রিটার্ন করা হয়েছে (সেটার নাম থাকুক বা না থাকুক), সেটা থেকেও কিন্তু আমরা কোনো একটা ভেরিয়েবল, array বা অন্য কিছু রিটার্ন করতে পারি। তাই আমরা সেটার ভিতর থেকে count ভেরিয়েবলকেই রিটার্ন করে ফেললাম।
function counter(){
let count = 0;
return function(){
count++;
return count;
};
}৮. এখন হবে আসল খেলা। ওপরের counter ফাংশনকে আমরা একেকজনের জন্য আলাদা আলাদাভাবে কল করতে পারি।
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:
- closure কী জিনিস?
- তুই একটা fridgeTracker() ফাংশন বানা এবং তোর মেসের সবার জন্য আলাদা আলাদা ক্লোজার বানা। যাতে যে যতবার ফ্রিজ ওপেন করবে, তার কাউন্টার তত বাড়বে।
- তুই একটা taskTracker() ফাংশন বানা, যেখানে প্রতিবার একটা কাজ ফিনিশ হওয়ার পর কাউন্ট এক এক করে বাড়বে। তোর বা তোর ফ্রেন্ড সবার কাজের স্কোপ আলাদা আলাদা থাকবে।