আমার সাথে

Personal Blog of Zonayed
ওয়েব টেকনোলোজি জাভাস্ক্রিপ্ট প্রোগ্রামিং বাংলা

জাভাস্ক্রিপ্ট অ্যাডভান্সঃ call(), bind() এবং apply() মেথড

জুলাই 7, 2018

গত পর্বে আমি this কীওয়ার্ড নিয়ে আলোচনা করেছি। সেখানে this কীওয়ার্ডের ভ্যালু বা অন্য কথায় this এর কন্টেক্সট(Context) ডিটারমাইন করার জন্যে চারটা রুলস এর কথা বলেছিলামঃ

১। গ্লোবাল রুলস

২। অবজেক্ট রুলস

৩। স্পষ্ট রুলস

৪। new কীওয়ার্ড রুলস

তার মধ্যে প্রথম দুইটা আলোচনা করা হয়েছে গত পর্বেই। এই পর্বে তিন নাম্বার রুলস নিয়ে আলোচনা করবো। এখানেই আমি call(), bind()apply() এই তিনটা মেথডকে পরিচয় করিয়ে দিয়েছিলাম। জাভাস্ক্রিপ্ট ভালো করে আয়ত্তে আনতে হলে এই তিনটা মেথড সম্পর্কে আপনার পরিষ্কার ধারণা থাকতে হবে। আজকে তাই আমি এগুলো নিয়ে বিস্তারিত লিখবো।

এই তিনটা মেথডই প্রথম আর্গুমেন্ট হিসাবে this কীওয়ার্ডের কন্টেক্সট বা ভ্যালু কি হবে সেটা নেয়। তারমধ্যে call() আর bind() আনলিমিটেড আর্গুমেন্ট নিতে পারে আর অন্যদিকে apply() মাত্র দুইটা আর্গুমেন্ট(প্রথমটা সবসময়েই thisএর ভ্যালু ডিটারমাইন করতে, আর দ্বিতীয়টা একটা অ্যারে) নেয়। এখন বুঝলাম প্রথম আর্গুমেন্ট this ডিটারমাইন করার জন্যে দেওয়া হয়, কিন্তু বাকী আর্গুমেন্টগুলা কিসের? হ্যাঁ বাকিগুলো আপনি যে ফাংশনের সাথে এই মেথডগুলো লাগাবেন সেই ফাংশনেরও আর্গুমেন্ট থাকতে পারে, সে আর্গুমেন্ট যতটাই হউক আপনি call() আর bind() এর ক্ষেত্রে একটার পর একটা দিতে পারবেন। অন্যদিকে apply() এর ক্ষেত্রে যে অ্যারেটা দিবেন সেটা হবে সেই ফাংশনের সবগুলো আর্গুমেন্টের অ্যারে। কনফিউজড হয়ে গেলে সমস্যা নাই আমি প্রত্যেকটা উদাহরনসহ নিচে দেখাবো। আশা করি পরিষ্কার ধারণা হয়ে যাবে।

আরেকটা ডিফারেন্স হলো call() , apply() আর bind() এর মধ্যে। যেখানে call() , apply() যে ফাংশনের সাথে ইউজ করবেন সেটা সাথে সাথে কল হয়ে যাবে। অন্যদিকে bind() সাথে সাথে ফাংশনটাকে কল করে না, বরং আপনি সেটা পরে যেকোনো সময় চাইলে নিজের মন মতো করে কল করতে পারবেন।

call() মেথডঃ

আগের পর্বে একটা উদাহরণ দিয়েছিলাম। সেইমটার উপরেই আজকে কাজ করবোঃ

var myCustomObj = {
   name: 'Zonayed Ahmed',
   age: 21,
   job: 'Student',
   anotherObj: {
      name: 'Ahmed Zonayed',
      value: function() {
         console.log('My name is ' + this.name);
      }
   }
}

এখানে যদি আমরা value() ফাংশনটাকে কল করি তাহলে My name is Ahmed Zonayed দেখাবে। মানে এখানে this এর ভ্যালু হচ্ছে anotherObj , আর তাই এভাবে আউটপুট পাবেন।

myCustomObj.anotherObj.value();

কিন্তু এখানে যদি আমরা চাই value() এর ভিতরের this এর কন্টেক্সট বা ভ্যালু হিসাবে myCustomObj সেট করতে তাহলে সেটা আমরা স্পষ্ট করে call() দিয়ে সেট করে দিতে পারি এভাবেঃ

myCustomObj.anotherObj.value.call(myCustomObj);

দেখুন এবার প্রিন্ট হয়েছে My name is Zonayed Ahmed, মানে this এর ভ্যালু এখানে আমরা আমাদের মতো করে চেঞ্জ করতে পেরেছি। আরেকটা জিনিস আমরা চাইলে call() টা আমাদের অবজেক্ট এর সাথেও দিতে পারতাম। কিন্তু ঐ যে বললাম call() যেখানে ইউজ করা হয় সেটা সাথে সাথে কল হয়ে যায়, তাই আপনি যেরকম আশা করবেন সেরকম রেজাল্ট নাও আসতে পারেঃ

var myCustomObj = {
   name: 'Zonayed Ahmed',
   age: 21,
   job: 'Student',
   anotherObj: {
      name: 'Ahmed Zonayed',
      value: function() {
         console.log('My name is ' + this.name);
      }.call(myCustomObj)
   }
}

এটা এভাবে রান করার পর পরই দেখবেন আপনার ফাংশন কল করা ছাড়াই প্রিন্ট হয়ে গেছেঃ

সেইমভাবে আপনার দুইটা সম্পূর্ন পৃথক পৃথক অবজেক্ট এর ক্ষেত্রেও আপনি এই মেথডগুলো কাজে লাগাতে পারবেন। যেমন ধরি আমাদের দুইটা অবজেক্ট আছে এরকমঃ

var karim = {
   name: 'Karim Rahman',
   dob: 1996,
   age: function(currentYear) {
      console.log(this.name + ' is ' + (currentYear - this.dob) + ' years old!');
   }
}

আরেকটাঃ

var rahim = {
   name: 'Rahim Abdu',
   dob: 1986
}

দেখুন প্রথম অবজেক্ট karim থেকে আমরা খুব সহজেই age ফাংশনটা কল করে karim এর বয়স জানতে পারবোঃ

karim.age(2018)

এখন লক্ষ্য করুন rahim এ আমাদের কিন্তু age নামক ফাংশনটা নাই, কিন্তু তারপরেও আমরা চাইলে এই age ইউজ করে এর সাথে call(), bind() বা apply() দিয়ে এর ভিতরের this এর ভ্যালু চেঞ্জ করে সেটা rahim এর জন্যেও ইউজ করতে পারিঃ

karim.age.call(rahim, 2018);

আরো দেখবেন এখানে দ্বিতীয় আর্গুমেন্ট হিসাবে age এর আর্গুমেন্ট হিসাবে ভ্যালু পাস করেছি। সেইম জিনসটা bind() আর apply() দিয়েও করা যাবে।

apply() মেথডঃ

এটাও সেইম টু সেইম call() এর মতোই, জাস্ট এটা দুইটা আর্গুমেন্ট নিবে আর দ্বিতীয় আর্গুমেন্ট টা আপনার ফাংশনের জন্যে যে কয়টা আর্গুমেন্ট থাকবে সেগুলার অ্যারে নিবেঃ

var myCustomObj = {
   name: 'Zonayed Ahmed',
   age: 21,
   job: 'Student',
   anotherObj: {
      name: 'Ahmed Zonayed',
      value: function() {
         console.log('My name is ' + this.name);
      }
   }
}

আগের এই সেইম উদাহরণে apply() ইউজ করলেও সেইম রেজাল্ট পাবেনঃ

myCustomObj.anotherObj.value.apply(myCustomObj);

call() থেকেapply() এর পার্থক্যটা দ্বিতীয় আর্গুমেন্ট নেওয়ার ক্ষেত্রে যেখানে apply() আর্গুমেন্ট এর অ্যারে নেয়। আগের rahim আর karim একটু মডিফাই করেঃ

var karim = {
   name: 'Karim Rahman',
   dob: 1996,
   age: function(currentYear, msg) {
      console.log(msg + ' ' + this.name + ' is ' + (currentYear - this.dob) + ' years old!');
   }
}

এবংঃ

var rahim = {
   name: 'Rahim Abdu',
   dob: 1986
}

karim এর age কল করলেঃ

karim.age(2018, 'Hello World!');

age ফাংশনটায় দুইটা আর্গুমেন্ট লাগিয়েছি বুঝার সুবিধার্থে। এটা রান করলে কন্সোলে পাবেনঃ

এখন এই সেইম age ফাংশন rahim এ ঠিক আগের মতো করে ইউজ করতে চাচ্ছি। কিন্তু এবার apply() দিয়েঃ

karim.age.apply(rahim, [2018, 'Hello World!']);

লক্ষ্য করুন এখানে দ্বিতীয় আর্গুমেন্ট টা একটা অ্যারে যেটা আপনার age ফাংশনের সবগুলো আর্গুমেন্ট নিয়েছেঃ

আশা করি এবার call() আর apply() মধ্যে তফাৎ টা ধরতে পেরেছেন। এবার চলি আসুন bind() নিয়ে কথা বলি।

bind() মেথডঃ

bind() ঠিক call() এর মতো হলেও যেখানে call() আরapply() সাথে সাথে যে ফাংশনের সাথে ইউজ করা হয়েছে সেটাকে কল করে ফেলে, bind() সেখানে সে ফাংশনকে কল করে না, বরং এটা সেই ফাংশনের আরেকটা ডেফিনেশন রিটার্ন করে যেটা পরবর্তিতে আপনি যেকোনো জায়গায় কল করতে পারবেন বা ইউজ করতে পারবেন। আগের সেইম উদাহরণেই যদি সেইমভাবে bind() ইউজ করি তাহলে পার্থক্যটা ধরতে পারবেনঃ

var myCustomObj = {
   name: 'Zonayed Ahmed',
   age: 21,
   job: 'Student',
   anotherObj: {
      name: 'Ahmed Zonayed',
      value: function() {
         console.log('My name is ' + this.name);
      }
   }
}

এবার সেইমভাবে value এর this এর ভ্যালু myCustomObj এ সেট করতে আগের মতোই bind() মেথড ইউজ করলেঃ

myCustomObj.anotherObj.value.bind(myCustomObj);

এটা দেখবেন সরাসরি আপনার কাঙ্ক্ষিত লেখা প্রিন্ট করছে না, বরং এটা যা রিটার্ণ করছে সেটা আরেকটা ফাংশন ডেফিনেশনঃ

এখন এই ফাংশনটাকে আপনি আরেকটা ভ্যারিয়েবলে স্টোর করে পরে যেকোনো সময়, যেকোনো জায়গায় ইউজ করতে পারবেনঃ

var anotherFunc = myCustomObj.anotherObj.value.bind(myCustomObj);

এবার এই ফাংশন যেখানে কল করবেন সেখানেই আপনার কাঙ্ক্ষিত ফলাফল আসবেঃ

anotherFunc();

আর এ জন্যেই গত পর্বে উদাহরনটায় আমরা bind() ইউজ করেছিলাম call() বা apply() ইউজ না করে। আর এজন্যেই এই তিনটা মেথডের মধ্যে এই bind() সবচেয়ে ইউজফুল।

bind() এ আপনি আর্গুমেন্টগুলোও পৃথক পৃথক ভাবে কল করতে পারবেন। ধরেন প্রথমে আপনি আপনার কিছু আর্গুমেন্ট দিলেন, পরে আবার ফাংশন কল করার সময় বাকি আর্গুমেন্টগুলো দিতে পারবেন। যেমন apply() তে ইউজ করা kahim আর rahim এর উদাহরণের ক্ষেত্রেঃ

var karim = {
   name: 'Karim Rahman',
   dob: 1996,
   age: function(currentYear, msg) {
      console.log(msg + ' ' + this.name + ' is ' + (currentYear - this.dob) + ' years old!');
   }
}

আরঃ

var rahim = {
   name: 'Rahim Abdu',
   dob: 1986
}

এই উদাহরণের ক্ষেত্রে যদি আমরা চাই msg এর ভ্যালু পরে সেট করতে তাহলে এটা এভাবেও করা যাবেঃ

var rahimAge = karim.age.bind(rahim, 2018);

লক্ষ্য করুন আমরা এখানে মাত্র একটা আর্গুমেন্ট দিয়েছি, আরেকটা দেই নাই। যেটা পরবর্তিতে আমরা এই ফাংশনটাকে কল করার সময় দিতে পারবোঃ

rahimAge('Hello World!');

বা আমরা চাইলে এটাকে আরো এক ধাপ আগায় নিতে পারি এভাবে। প্রথমে জাস্ট bind() দিয়ে আরেকটা ফাংশন বানাইলাম যেটার কাজ হবে rahim এর age ক্যালকুলেট করাঃ

var rahimAgeCalculate = karim.age.bind(rahim);

এখন এই rahimAgeCalculate ফাংশনে বাকি আর্গুমেন্ট গুলা পাস করতে পারবোঃ

rahimAgeCalculate(2018, 'Hello Dolly!');

যতবার যতভাবে ইচ্ছাঃ

rahimAgeCalculate(2028, 'Hello Ahmed!');
rahimAgeCalculate(2028, 'Hello Zonayed!');
rahimAgeCalculate(2050, 'Hi!');

এখানেই এই bind() একটু স্পেশাল আর ইউজ কেসও বেশী এটার।

স্পেশাল উদাহরণঃ

নিচের এই উদাহরনে this এর ভ্যালু কি হতে পারে অনুমান করার চেষ্টা করুনঃ

var myObj = {
   name: 'Zonayed Ahmed',
   age: 21,
   timer: function() {
      setTimeout(function() {
         console.log('My name is ' + this.name);
      }, 1000)
    }
}

এখান আমরা timer ফাংশনটাকে কল করলেঃ

myObj.timer();

এটা দ্বিতীয় রুলস(অবজেক্ট রুলস) এ পড়ে যেহেতু এই ফাংশনটা একটা কাস্টমভাবে ডিফাইন করা অবজেক্ট এর ভিতরে আছে। তাহলে সে হিসাবে এটার ভিতরের this আমার সেই অবজেক্ট myObj কেই ইন্ডিকেট করার কথা। কিন্তু এটা এক্সিকিউট করলে ১০০০ মিলিসেকেন্ড পরে এমন আউটপুট আসবেঃ

কোনো কারনে name এর ভ্যালু আসছে না, তারমানে এখানে তাহলে কোথাও একটা প্রবলেম হচ্ছে। আচ্ছা তাহলে ঠিক এখানে this এর ভ্যালু কি দেখে নেওয়া যাকঃ

var myAnotherObj = {
   name: 'Zonayed Ahmed',
   age: 21,
   timer: function() {
      setTimeout(function() {
         console.log(this);
      }, 1000)
    }
}

এখানে timer ফাংশনটাকে কল করলেঃ

myAnotherObj.timer();

১০০০ মিলিসেকেন্ড পরে প্রিন্ট করবেঃ

এখানে কোনো কারনে this গ্লোবাল অবজেক্ট(ব্রাউজারের ক্ষেত্রে window অবজেক্ট) কে ইন্ডিকেট করছে। কেনো? হ্যাঁ আপনি যদি আপনার গ্লোবাল অবজেক্ট ওপেন করে দেখেন দেখবেন এই setTimeout আসলে সেই গ্লোবাল অবজেক্ট এর একটা মেথডঃ

console.dir(window);

এটা ওপেন করলে অনেকগুলো মেথড পাবেন। তারমধ্যে setTimeout ও পাবেনঃ

তো আমাদের কথামতো setTimeout এর ভিতরে this তাই গ্লোবাল অবজেক্ট কেই ইন্ডিকেট করছে। যদিও setTimeout আরেকটা কাস্টমভাবে ডিফাইনকৃত অবজেক্ট এর ভিতরে কিন্তু এটার ভিতরে থাকা this এর সবচেয়ে কাছের অবজেক্ট হচ্ছে গ্লোবাল অবজেক্ট যেহেতু setTimeout আসলে গ্লোবাল অবজেক্ট এরই একটা মেথড। তাই এভাবে একটার ভিতরে আরেকটা মেথড থাকলেও আপনার this এর ভ্যালু চেঞ্জ হয়ে যেতে পারে।

এখন আমরা যেহেতু call(), apply(), bind() মেথডগুলো জানি তাই চলেন আমরা এটা ফিক্স করে ঠিক যেটা প্রিন্ট করাতে চাচ্ছিলাম সেটাই প্রিন্ট করাইঃ

var myObj = {
   name: 'Zonayed Ahmed',
   age: 21,
   timer: function() {
      setTimeout(function() {
         console.log('My name is ' + this.name);
      }.bind(myObj), 1000)
    }
}

এখন timer ফাংশনটাকে কল করলেঃ

myObj.timer();

এখন এখানে call() বাapply() কেনো ইউজ করলাম না? হ্যাঁ এই দুইটা মেথড ইউজ করা যাবে কিন্তু আমরা যেভাবে আশা করছিলাম সেরকম রেজাল্ট আসবে নাঃ

var myObj = {
   name: 'Zonayed Ahmed',
   age: 21,
   timer: function() {
      setTimeout(function() {
         console.log('My name is ' + this.name);
      }.call(myObj), 1000)
    }
}

setTimeout দিয়ে আমরা চাচ্ছি ১০০০ মিলিসেকেন্ড পরে উক্ত লেখাটা প্রিন্ট হউক, কিন্তু call() ইউজ করায় সেটা সাথে সাথেই কল হয়ে যাবেঃ

myObj.timer();

দেখবেন আপনার কন্সোলে সাথে সাথে প্রিন্ট হয়ে গেছে, কন্সোল ১০০০ মিলিসেকেন্ড ওয়েট করে নাইঃ

সেইম apply() ক্ষেত্রেও হবে। তাই ক্ষেত্রবিশেষে এদের বিহেভিয়ার অনুযায়ী আপনাকে যেসময় যেটা দরকার সেটা ইউজ করতে হবে।

তো আজকে এই পর্যন্তই, ভালো থাকবেন আর পাশের মানুষটিকে ভালো রাখবেন।


আমার নতুন ব্লগ পোস্ট গুলোর আপডেট পেতে আপনি আপনার ইমেইল দিয়ে আমার ব্লগ পোস্টগুলো সাবস্ক্রাইব করে রাখতে পারেন, নতুন পোস্টগুলো সপ্তাহে একদিন আপনার ইনবক্সে চলে যাবে

Comments

comments