JavaScript Closures are not that scary!
নিচের কোডটি লক্ষ্য করুন
function sum(num1, num2) { return num1 + num2; } sum(2, 3); // output -> 5
আমরা কি দেখতে পাচ্ছি একটা sum
function যেটা প্যারামিটার হিসাবে দুইটা ভ্যালু পায় এবং শেষে তা যোগ করে দেয়। কিন্তু যদি আমরা function parameter অথবা function block এ কোনো ডাটা না দিয়ে গ্লোবালি ভেরিয়েবল ডিফাইন করে return num1 + num2;
এটা রিটার্ন করতে চাই তাহলে কি কাজ করবে ?
var num1 = 2; var num2 = 3; function sum() { return num1 + num2; } sum(); // output -> 5
কথা হলো কাজ করবে। কিন্তু ফাংশন কিভাবে এটা করতে পারলো তার মধ্যে তো ভেরিয়েবল গুলো নেই। পূর্বে স্কোপ থেকে জানি যে Child চাইলে তার পেরেন্ট এর Variable ব্যবহার করতে পারে এটা আমরা জানি । মেনে নিচ্ছি এটায় জাভাস্ক্রিপ্ট এর নিয়ম। কিন্তু জাভাস্ক্রিপ্ট এটা কিভাবে করে এটা জানতে গেলেই আমাদের সামনে আসবে closure
। এখন আমরা এটা নিয়ে বিস্তারিত জানার চেষ্টা করব।
এখানে যে স্কোপ টা কাজ করছে একে আমরা শুধু স্কোপ ও বলত পারি তবে জাভাস্ক্রিপ্ট একে একটা নাম দিয়েছে সেটা হলো লেক্সিকেল স্কোপ
Closures
উপরের ছবি টি ভালো করে লক্ষ্য করুন। দেখুন যখন একটা ফাংশন এর মধ্যে আমরা একটা ভেরিয়েবল কে ব্যবহার করতে চাচ্ছি কিন্তু তার মধ্যে ওইটার কোনো অস্তিত্বই নাই তাহলে অই ভেরিয়েবল টা কই থেকে ভেরিয়েবল টা পাই । এখন যদি আমরা function টা কে console.dir(sum)
করি তাহলে একটু এক্সপান্ড করলে দেখতে পাই যে সবার শেষে স্কোপ এর মধ্যে একটা Global নামে একটা Object আছে। অর্থাৎ প্রত্যেক টা ফাংশনই তার পেরেন্ট এবং তার পেরেন্ট এবং তার পেরেন্ট এর ডাটা গুলার একটা রেফারেন্স নিয়ে চলে। যখন প্রয়োজন তখন তা ব্যবহার করে। এটা ও One kind of কি Closure. এই জন্য তখন বলা হয়েছিলো যে Child যখন প্রয়োজন তখনই Parent সব কিছু অ্যাক্সেস করতে পারে কিন্তু Parent - Child এর কোনো কিছু অ্যাক্সেস করতে পারে না।
- এবার আবার একটা ছবি লক্ষ্য করুন
যদি আমরা sum function থেকে return করি আরেকটা ফাংশন এবং অই ফাংশন টা যদি return করে return num1 + num2
তাহলে আমরা একটা বিষয় আরো ও ক্লিয়ার হতে পারব। আর হে এখানে num2
কে আমরা sum ফাংশন এর মধ্যে রাখলাম তাহলে আমরা console.dir(sum())
কি দেখতে পাচ্ছি একটা anonymous function কারন আমরা তো একটা ফাংশন কে return করলাম। আচ্ছা এবার এর Global কে চেক করলে আমরা দেখতে পারবো যে এর স্কোপস্ এর মধ্যে দুইটা জিনিস আছে একটা হলো Clouser
& Global
আচ্ছা দেখুন তার মানে যেকোনো ফাংশন যে তার পেরেন্ট ভ্যালু গুলার একটা রেফারেন্স রাখে তা প্রমাণিত।
এখন রিটার্ন করা ফাংশন টা যেহেতু num2
কে ব্যবহার করছে কিন্তু তার মধ্যে ভ্যালুটা নাই, আছে তার পেরেন্ট এ, তাহলে রিটার্ন কৃত ফাংশন টা num2
এর রেফারেন্স পাবে ক্লোজার থেকে। যেটা আমরা ছবি তে দেখতে পারতেছি।
আর রিটার্ন করা ফাংশন টা যদি এমন কোনো ভ্যালু ব্যবহার করে যেটা গ্লোবালিই আছে তাহলে তো আর ক্লোজার রাখার কোনো মানেই নাই সেটা গ্লোবাল থেকে অ্যাক্সেস হবে।
যেটা আমরা দেখতেছি num1
এর ক্ষেত্রে।
বাংলায় ক্লোজারের ডেফিনেশনঃ
ক্লোজার হচ্ছে একটা মেমোরি। যদি কোনো ফাংশন এর ভিতর ব্যবহারকৃত ভ্যালু তার নিজের দুনিয়ায় না থাকে অর্থাৎ তার পেরেন্ট এ থাকে তবে অই পেরেন্ট এর ডাটা গুলো নিজের মধ্যে ক্লোজ করে রাখে ক্লোজার।
চলুন আরেকটা উদাহরণ দেখা যাকঃ
ধরুন আপনি একটা ব্যবসা করবেন কিন্তু আপনার কোনো টাকা নাই কিন্তু আপনার কাছে টাকা নাই । কিন্তু আপনি আপনার বন্ধুর টাকা কাজে লাগাইতে চান। এখন আপনার বন্ধু বলল আমি তোকে টাকা দিবো যদি তুই ব্যবসা করস। অর্থাৎ দিয়ে ও দিলো না অর্থাৎ আমি নিচে ছবির মতো যদি capital টা কাজে লাগায় তাহলেই অইটা আমি ক্লোজার এ পাব।
আর যদি কাজে না লাগায় তাহলে নিচের মতো এটা কে ক্লোজারে পাবো না।
উদাহরণ কাল্পনিক বোঝার জন্য । মোটকথা জাভাস্ক্রিপ্ট এই বেপার টা স্মার্টলি হ্যান্ডেল করে যদি আমার কাজে কোনো ভেরিয়েবল ব্যবহার করি তা যদি আমার পেরেন্ট এ থাকে তাহলে তাকে আমার ক্লোজার এ পাই । আর যদি কোনো ভেরিয়েবল ব্যবহার না করি তা যদি আমাদের পেরেন্ট এর মধ্যে ও থাকে তা তার ক্লোজার এ যাবে না। এটায় ।
এটা এমন কিছু না যে না জানলে আমাদের জীবন চলবে না আমার অনেক কাজে ক্লোজার ব্যবহার করি করি কিন্তু আমরা জানি না যে ক্লোজার কে ব্যবহার করছি। কিন্তু এটা ইন্টারভিউ তে কমন প্রশ্ন । এটা একটা জাভাস্ক্রিপ্ট এর ফান্ডামেন্টাল। এর জন্য আমার বুঝতে পারি জাভাস্ক্রিপ্ট কিভাবে কাজ করে অনেক ক্ষেত্রে আমাদের কোড ডিবাগ করতে ও কাজে আসতে পারে।
UseCase of Closures
###Global one kind of Closures
Timestamp
Video Link Click here
Closure store value reference
What else if we use store by let without var
যদি আমরা let
ব্যবহার করি তাহলে আমরা দেখতে পাবো num1 , num2
ভেরিয়েবল গুলো চলে যাবে Script
নামে একটা অবজেক্ট এ কারন আমার জানি let হচ্ছে ব্লক স্কোপ তারা আমাদের .js
ফাইল এর ব্লক এ আছে তাদেকে গ্লোবাল এ পাবো না। পাবো আরেকটা Script
অবজেক্ট এ ।
another difference when let stay in enclosing
এখানে দেখুন যদি let
দিয়ে কোনো ভেরিয়েবল তা কোনো এনক্লোজিং এর মধ্য থাকে এক্ষেত্রে ভেরিয়েবল গুলো এমন একটা ফাংশন এ রাখা হয়েছে যা নিজেই নিজেকে কল করে। এখন আবার এটা আগের নিয়মে ভেরিয়েবল গুলো ক্লোজারে ঢুকে যাবে।
Example
function stopWatch() { const startTime = Date.now(); function getDelay() { console.log(Date.now() - startTime); } return getDelay; } const timer = stopWatch(); // fact delay for (let i = 0; i < 10000000; i++) { var a = Math.random() * 100000000; } timer(); // random a number - 162
এখানে লক্ষ্য করুন stopWatch()
ফাংশন টা যখন কল হয় তখন একটা টাইম স্টার্ট হয় এবং আরেকটা ফাংশন রিটার্ন করে যা কিনা যখন সে কল হয় তা থেকে স্টার্ট টাইম টা বাদ দিয়ে আউটপুট দেখায়। এখানে মাঝখানে আমরা একটা ফেক ডিলে তৈরি করি পরে আমার আবার timer()
কে কল করি সে কিন্তু ঠিকঠাক আউটপুট দিচ্ছে। কিভাবে সামান্য ডিলের পরে ও startTime
এর ভ্যালু ঠিকঠাক থেকে যাচ্ছে। এখানেও ক্লোজার ব্যবহার হচ্ছে । যেহেতু startTime
ভেরিয়েবলটা ভিতরের ফাংশনে ব্যবহার হয়েছে কিন্তু তার স্কোপ এ নাই কিন্তু তার পেরেন্ট এ আছে তাই তাকে সে তার ক্লোজার যারে ধরে রাখবে। এটা একটা বেস্ট উদাহরন হতে পারে।
Application Optimaization
উপরের ছবিতে লক্ষ্য করুন আমরা যখন timer
কে কল করছি তখন ঠিক আছে কিন্তু তার পর ও কিন্তু আমাদের ক্লোজার এ ডাটা থেকে যাচ্ছে। এটা তো যাস্ট একটা ভেরিয়েবল তাই সমস্যা হচ্ছে না কিন্তু বড় অ্যাপ্লিকেশন এ তো আর ও অনেক অনেক ডাটা থাকতে পারে এতে করে কি ক্লোজার এর এগুলা সেট থাকবে। হে জাভাস্ক্রিপ্ট হচ্ছে গার্ভেজ কালেক্টর প্রোগ্রামিং ল্যাঙ্গুয়েজ সে নিজে নিজে অনেক কাজ এ করে নেয় কিন্তু আমাদের এখানে উপরের ছবিতে পরে আবার আমরা timer
কে কল করতে পারি তাই সে এটাকে রিমোভ করবে না। এখানে ডেভেলোপার চাইলে আরও অপটিমাইজ করতে পারে।
আমাদের কল করার কাজ শেষ হয়ে গেলে আমরা timer = null
করে দিতে পারি। এতে করে আমাদের ডাটা আর ক্লোজারে স্টোর থাকবে না। এটা যাস্ট ছোট্ট একটা অপটিমাইজেশন এর উদাহরণ যে চাইলেই এভাবে করা যেতে পারে।
Closure store value by reference
এখানে দেখুন যদি Asynchronous
কোনো কোড থাকে তাহলে তার তো একটু টাইম লাগে এর মধ্য যদি আমরা ভেরিয়েবল চেঞ্জড করে দেই তাহলে আমরা মূলত চেঞ্জড ডাটাই দেখতে পারব। ছবিতে খেয়াল করুন। তাহলে বুঝা যাচ্ছে ক্লোজার ভেরিয়েবল কে সেভ রাখে না রাখে তার রেফারেন্স যার ফলে যখন যে কল হয় তখন যদি আমাদের অ্যাপ এ কোনো কারনে অ্যাক্সেডেন্টালি আমাদের ভেরিয়েবল এর ভ্যালু চেঞ্জড হয়ে যায় তাহলে আমার আমাদের সঠিক আউটপুট নাও পেতে পারি। তাই আবিষ্কার হয়েছে let
তাই আমরা এইক্ষেত্রে let
কে ব্যবহার করব।
Another Closures example
function fetchFunc(url) { fetch(url) .then((res) => res.json()) .then((data) => console.log(data)); } fetchFunc('https://jsonplaceholder.typicode.com/users');
এখানে ও ক্লোজার ব্যবহার হচ্ছে fetch
যে url
কে ব্যবহার করছে। সেটা সে ক্লোজার এ রেখে দিচ্ছে। যদি আমার url
কে ব্যবহার না করতাম তাহলে জাভাস্ক্রিপ্ট স্মার্ট ভাবে তাকে আর Carry করত না।
Common interview quesition
যদি আমরা let
ব্যবহার করি
for (let i = 0; i < 3; i++) { setTimeout(() => { console.log(i); }, 3000); } /* output -> 0 1 2 */
কিন্তু আমরা যদি var
ব্যবহার করি
for (var i = 0; i < 3; i++) { setTimeout(() => { console.log(i); }, 3000); } /* output -> 3 3 3 */
কেনো এমন হচ্ছে ??
উত্তরঃ আসলে যখন আমরা var
ব্যবহার করি তখন তা আসলে আর ক্লোজারে থাকে না মূল কথা তখন আমাদের ভেরিয়েবল ডিফাইন হয় গ্লোবালে। তাই আমাদের setTimout
কিন্তু 3 secound
সময় নিবে এর মধ্য কিন্তু for loop
শেষ হয়ে i
এর ভ্যালু 3 হয়ে যাবে কারন for
Synchronous code আর i
অই গ্লোবাল এর রেফারেন্স কে ধরে রাখবে। তাই যখন Asynchronous কোড হবে তখন দেখবে i
এর ভ্যালু 3
সে তাই প্রিন্ট করবে।
var i; // which Declare in global for (i = 0; i < 3; i++) { setTimeout(() => { console.log(i); // here i contain the global reference value }, 3000); }
কিন্তু যদি let
ব্যবহার করি তখন কিন্তু তা হবে না। কারন let
হচ্ছে ব্লক স্কোপ। যখন এ লুপ চলবে তখনই সে প্রতিবারে নতুন রেফারেন্স এ নতুন ভ্যালু অ্যাড করবে। এতে করে যখন Asynchronous কোড রান করবে তখন ফাংশন এর ক্লোজার থেকে তাদের আলাদা আলাদা ভ্যালু কে প্রিন্ট করবে।
for (let i = 0; i < 3; i++) { setTimeout(() => { console.log(i); // here i get difference reference value from the function closure }, 3000); }
আমরা এভাবে এটাকে চেক করতে পারি
for (let i = 0; i < 3; i++) { const myFunc = () => { console.log(i); }; console.log(i); console.dir(myFunc); // আমরা এখানে ৩ টা আলাদা আলাদা myFunc পাবো যাদের ক্লোজারে আলাদা আলাদা ভ্যালু থাকবে। setTimeout(myFunc, 3000); }
Closure is just a function which remain remember value. When any function use any variable or whatever is it firstly he find his local variable in his scope and then he don't get it then he find the variable from his parent and parent and parent then the function find the value then he contain the value is his closure