২। Scope
Closure কোন ফাংশন না আবার ফাংশনও কোন closure না। Closure হচ্ছে ফাংশনের এমন একটা বৈশিষ্ট্য যে বৈশিষ্ট্যের কারণে ফাংশন এক্সিকিউশন শেষ হয়ে যাবার পরেও তার lexical scope এ অবস্থিত সকল variable কে মনে রাখতে পারে। উদাহরণস্বরূপ বলা যেতে পারে যে ডম থেকে কিছু অ্যাক্সেস করার জন্যে আমরা যে ইভেন্ট ফাইয়ার করি সেটাও একটা closure।
function add(a) {
return function (b) {
return a + b;
};
}
let addTen = add(10);
let addSeven = addTen(7);
console.log(addSeven); // 17
কি হচ্ছে এসব?? ঠিক আছে, চলেন দেখি কোডগুলোকে ভেঙ্গেঃ-
১। যখন add ফাংশনটি কল হয় এটি আরেকটি ফাংশনকে return করে। ২। ঐ ফাংশনটির এক্সিকিউশন শেষ হয়ে যায় এবং মনে রাখে ঐ সময় তার প্যারামিটার a এর ভ্যালু কি ছিল। ৩। যখন addTen ভেরিয়েবলে add ফাংশনকে এসাইন করা হয়। এটি সব সময় মনে রাখবে a এর ভেল্যু কি ছিল যখন এটিকে ইনিশিয়ালি কল করা হয়েছিল। ৪। উপরের addTen ভেরিয়েবল একটি ফাংশনকে বোঝায় যেটি সব সময় ভেল্যু ১০ যোগ করবে যা পাঠানো হয়েছিল। ৫। তার মানে হল যখন addTen কে কল করা হয় ৭ ভেল্যু দিয়ে, এটি ১০ এর সাথে ৭ যোগ করবে এবং ১৭ রিটার্ন করবে।
সুতারং, জাভাস্ক্রিপ্ট ইঞ্জিন addTen কে যেভাবে রান করেঃ-
function addTen(b) {
return 10 + b;
}
এখন একটা মজার উদাহরণ দেখবো। কিভাবে আমরা লুপের ভিতরে ক্লোজার চালাতে পারি। এটি ইন্টার্ভিউ বোর্ডের একটা কমন প্রশ্ন। নিচের কোডটা দেখেন এবং একটু মনে মনে চিন্তা করেন এটার আউটপুট কত হবে।
for (var i = 1; i <= 5; i++) {
setTimeout(() => console.log(i), 1000);
}
কাঙ্খিত আউটপুটঃ-
1
2
3
4
5
কিন্তু আসছে অনাকাঙ্ক্ষিত আউটপুটঃ-
6
6
6
6
6
আসলে এই আউটপুট আসার অনেক কারণ আছে। লুপের মাঝে ভ্যারিয়েবল i হচ্ছে একটি গ্লোবাল ভ্যারিয়েবল। যখন setTimeout রান হয় তার আগেই লুপ শেষ হয়ে যায় এবং তাই i ভ্যালু 6 হয়ে যায়। সেজন্যে প্রতি এক সেকেন্ড পর পর পাঁচবার 6 দেখাচ্ছে। যদি বিশ্বাস না হয় তাহলে কোডটা রান করার পর আপনার গ্লোবাল window অবজেক্টটা একবার দেখেন সেখানে i নামে একটা ভ্যারিয়াবল দেখতে পারবেন এবং তার ভ্যালু 6 হয়ে আছে।
এই সমস্যার সমাধান আমরা IIFE বা Immediately Invoked Function Expression ব্যবহার করে করতে পারি। নিচে উদাহরণ দেওয়া হলোঃ-
পদ্ধতি ১ঃ-
for (var i = 1; i <= 5; i++) {
(function () {
var val = i;
setTimeout(() => console.log(val), 1000);
})();
}
পদ্ধতি ২ঃ-
for (var i = 1; i <= 5; i++) {
(function (val) {
setTimeout(() => console.log(val), 1000);
})(i);
}
এখানে আমরা একটা ফাংশন লিখে একটা Scope তৈরি করেছি। ফাংশনটিকে ইমিডিয়েটলি কল করেছি এবং তার প্যারামিটারের ভেল্যু হিসাবে i কে পাস করেছি। এতে সে এখন i এর ভেল্যুকে মনে না রেখে সে এখন তার প্যারামিটারের ভেল্যুকে মনে রাখবে। মানে এখন i এর মান 1, 2 করে যাচ্ছে এবং সেটা থেকে একটা আলাদা Scope তৈরি হচ্ছে যেটাকে সে মনে রাখছে।
পদ্ধতি ৩ঃ-
for (let i = 1; i <= 5; i++) {
setTimeout(() => console.log(i), 1000);
}
আউটপুটঃ-
1
2
3
4
5
অবশেষে আমাদের কাঙ্ক্ষিত আউটপুট পেলাম। তবে আজ এই পর্যন্ত দেখা পরবর্তী অন্য টপিকে। হ্যাপি কোডিং...