JavaScript Prototype Inheritance & ES6 Class
প্রত্যেকটি ফাংশন এর প্রটোটাইপ থাকে।
জাভা ও অন্যান্য class
বেস প্রোগ্রামিং লাঙ্গুইয়েজে আমরা অব্জেক্ট তৈরি করে থাকি class
দিয়ে। কিন্তু জাভাস্ক্রিপ্ট হচ্ছে ফাংশনাল প্রোগ্রামিং ল্যাঙ্গুইয়েজ। এখানে অব্জেক্ট তৈরি হয় কন্সট্রাক্টর দিয়ে। আর জাভাস্ক্রিপ্ট প্রত্যেকটি ফাংশনই একেকটি কন্সট্রাক্টর। চলুন একটা কন্সট্রাক্টর ফাংশন নি এবং তাকে একটা ভেরিয়েবল এ রেখে console.dir()
করি ।
var f = function Person() {}; console.dir(f);
দেখুন আমাদের person
ফাংশন এর প্রোটোটাইপ এর মধ্যে আরেকটা প্রোটোটাইপ আছে তার মধ্যে আছে মাস্টার অব্জেক্ট অর্থাৎ Object
। জাভাস্ক্রিপ্ট এ এর মাস্টার অব্জেক্ট থেকে সকল অব্জেক্ট তৈরি হয়ে থাকে। আমাদের উপরের ফাংশন ক্রিয়েশন টা যদি একটু ভিজুয়ালাইজ করি তাহলে দেখতে অনেকটা এইরকম হবে।
এখানে মাস্টার অব্জেক্ট থেকে আমাদের ফাংশন এবং আমাদের ফাংশন থেকে f ক্রিয়েট হয়েছে। এখানে প্রত্যেকটি জিনিসের prototype
আছে এবং তাদের আলাদা আলাদা prototype
এর নিজস্ব properties
and method
আছে। এই যে চেইন এর মতো বেপার টা একে বলা হয় প্রোটোটাইপ চেইন।
এখন সর্বশেষ f
ফাংশন থেকে যদি আমার আরেকটা অব্জেক্ট তৈরি করি তাহলে আমার অই অব্জেক্ট থেকে তার পেরেন্ট তার পেরেন্ট অর্থাৎ মাস্টার অব্জেক্ট পর্যন্ত সকল প্রোপার্টিস এবং মেথড গুলো ব্যবহার করতে পারব।
চলুন আমাদের নিজের করা একটা ফাংশন বানানো যাক যাকে আমরা মাস্টের অব্জেক্ট এ সেট করে দিবে যার ফলে আমরা তাকে যেকোনো স্থান থেকে তাকে ব্যবহার করতে পারব।
Object.prototype.myLog = function () { console.log('This is my home made log'); }; let p = {}; p.myLog(); // output - This is my home made log
তাহলে বেপার বুঝা যাচ্ছে। এটা হচ্ছে প্রোটোটাইপ চেইন এর কারনে, p ফাংশন তার স্পেস না পেয়ে তার পেরেন্ট থেকে ইনহেরিট করে আমাদের দিয়ে দিচ্ছে।
Inheritance
চলুন আজকের মেইন টপিক ইনহেরিটেন্স এ যাওয়া যাক। আসলে ইনহেরিটেন্স এ যাবার আগে জাভাস্ক্রিপ্ট এর এই প্রোটোটাইপ টা জানা খুবই গুরুত্বপূর্ণ কারন ব্যাকেন্ড এ প্রোটোটাইপ দিয়ে সব হচ্ছে।
Usecase of inheritance
ধরুন আমাদের একটা person
অব্জেক্ট লাগবে তো আমরা তৈরি করলাম।
function Person(name, age) { this.name = name; this.age = age; } Person.prototype = { eat() { console.log(`${this.name} am eating`); }, }; const sakib = new Person('Sakib', 35);
এখন আমাদের আরেকটি অব্জেক্ট লাগবে ক্রিকেটার।
function Cricketer(type, country) { this.type = type; this.country = country; }
এখন দিনশেষে ক্রিকেটারও তো একজন Person
এখন আমরা কি করব Person
থেকে ইনহেরিট করে ক্রিকেটার অব্জেক্ট এ Person
সকল বৈশিষ্ট্য গুলো নিয়ে এনে ব্যবহার করব। এটাই হচ্ছে ইনহেরিটেন্স এখন এটা জাভাস্ক্রিপ্ট এ একরকম এবং জাভা অন্যান্য লাঙ্গুইয়েজে অন্য রকম তাই জাভাস্ক্রিপ্ট এ আমাদের কে তার ওয়েতে চিন্তা করতে হবে।
এখন চলুন Person
থেকে ইনহেরিট করে ক্রিকেটার অব্জেক্ট এ Person
সকল বৈশিষ্ট্য গুলো ব্যবহার করার কানেকশন টা করে নিই।
function Person(name, age) { this.name = name; this.age = age; } function Cricketer(name, age, type, country) { Person.call(this); this.name = name; this.age = age; this.type = type; this.country = country; } Person.prototype = { eat() { console.log(`${this.name} am eating`); }, }; const sakib = new Person('Sakib', 35);
যেহেতু আমরা Cricketer
এর মধ্যে Person
কে পেতে চাচ্ছি। তাই আমরা Cricketer
এ Person
কে Person.call()
করে দিবো এবং সাথে this
কে পাঠায় দিবো। এই this
হচ্ছে Cricketer
এর সকল ডাটা।
এখন এই কাজ টা করে আমরা Person
এর সকল ইনহেরিট করে নিয়ে আসলাম কিন্তু Person.prototype
এর কোনো কিছু নিয়ে আশা হলো না চলুন এই কাজটা Object.create()
দিয়ে করে ফেলে। কারন আমরা জানি Object.create()
কি করে তাকে দেওয়া কোনো অব্জেক্টকে বেস করে সে আরেকটা চাইল্ড অব্জেক্ট বানায় যা আমাদের দিয়ে অব্জেক্ট থেকে অ্যাক্সেস পায়। চলুন এখানে Person.prototype
কে Cricketer.prototype
এর প্রোটোটাইপ এ রেখে দিই।
Cricketer.prototype = Object.create(Person.prototype);
এখানে আমাদের আরেকটি কাজ করতে হবে সেটা হলো আমরা জানি জাভাস্ক্রিপ্ট এ অব্জেক্ট ক্রিয়েট হয় কন্সট্রাক্টর এর মাধ্যমে এখানে Cricketer
এর কন্সট্রাক্টর এ যে আমরা Person.call(this)
করে Person
এর ডাটা গুলো আনলাম এটা তো Cricketer
জানে না তাই আমাদেরকে Cricketer
এর কন্সট্রাক্টর কে অভাররাইট করতে হবে। এভাবে,
Cricketer.prototype.constructor = Cricketer;
এবার যদি সাকিব একটা অব্জেক্ট বানাই।
const sakib = new Cricketer('Sakib', 34, 'Cricketer', 'Bangladesh'); console.log(sakib.name); // output - Sakib consol.log(sakib.eat()); //output - sakib is eating
Summary
যদি আমরা প্রোটোটাইপ এর মাধ্যমে কোনো পেরেন্ট অব্জেক্ট থেকে অন্য অন্য অব্জেক্ট এই ক্ষেত্রে এটা চাইল্ড বলা যায়, সকল কিছু ইনহেরিট করে নিয়ে আসতে চাই তাহলে আমাদের যা যা করতে হবে।
যেখানে ইনহেরিট করতে চাই সেখানে
childObject.call(this)
করতে হবে।- তার কন্সট্রাক্টর কে আপডেট করতে হবে।
- প্রোটোটাইপ এর মধ্যে পেরেন্ট এর প্রোটোটাইপ কানেকশন করে দিতে হবে।
ইন দ্যা ব্যাকেন্ড জাভাস্ক্রিপ্ট এভাবে ইনহেরিট করে থাকে। আসলে আমার কোডিং এর সময় এভাবে করবো না। জাভাস্ক্রিপ্ট Es6
থেকে Class
সাপোর্ট করে যা আর কিছুই না জাস্ট সিনট্যাক্সটেকটিক্স সুগার মাত্র। আমার Class
ব্যবহার করলেও জাভাস্ক্রিপ্ট এভাবেই কাজ করে। চলুন দেখা যাক, জাভাস্ক্রিপ্ট এ কিভাবে সহজ ভাবে Class
ব্যবহার করে উপরের কাজটি করা যায়।
class Person { constructor(name, age) { this.name = name; this.age = age; } eat() { console.log(`${this.name} is eating`); } } class Cricketer extends Person { constructor(name, age, type, country) { super(name, age); this.name = name; this.age = age; this.type = type; this.country = country; } play() { console.log(`${this.name} is playing`); } } const sakib = new Cricketer('Sakib', 34, 'Cricketer', 'Bangladesh'); console.log(sakib.name); // output - Sakib consol.log(sakib.eat()); //output - sakib is eating
এখানে সিম্পল ক্লাস এর সিনট্যাক্স্টেক্স দিয়ে কনর্ভাট করা হয়েছে। এখানে ক্লাস সিনট্যাক্স্টেক্স বুঝানো আমার উদ্দেশ্য নয় আপনারা class syntax এখানে থেকে শিখে নিতে পারেন।
এখানে বুঝার বেপার হলো class Cricketer extends Person
এভাবে Person
থেকে ইনহেরিট করা হলো। যেটা কে ক্লাস এর ভাষায় এক্সটেন্ড করা ও বলে। এবং Person
কে Cricketer
এ কল করার জন্য Cricketer
এর মধ্যে super
কে কল করে দেওয়া হয়। এভাবে জাভাস্ক্রিপ্ট এ ক্লাস ব্যবহার করে ইনহেরিটেন্স করা হয়।
getter & setter
class Person { constructor(name, age) { this.name = name; this.age = age; } eat() { console.log(`${this.name} is eating`); } get getName() { //getter return this.name; } set getName(name) { //setter return (this.name = name); } } const sakib = new Person('Sakib', 34); console.log(sakib.getName); //output - Sakib sakib.getName = 'Sakib Al Hasan'; console.log(sakib.getName); //output - Sakib Al Hasan
getter
তেমন কিছুই না কন্সট্রাক্টর অব্জেক্ট থেকে কোনো প্রোপার্ট অ্যাক্সেস করার জন্য ব্যবহার করা হয়। setter
এর মাধ্যমে আমরা কন্সট্রাক্টর অব্জেক্ট এর কোনো ভ্যালু চেঞ্জড করে দিতে পারি। কিন্তু এগুলো ফাংশন এর মতো দেখতে হলেও এগুলো ইনবোক করার সিস্টেম টা কিন্তু আলাদা। উপরের console.log()
গুলো দেখুন।
তাহলে প্রশ্ন আসতে পারে যে তাহলে কেন আমরা এগুলো ব্যবহার করব।
ফাংশন আর getter & setter
এর মধ্যে পার্থক্য কি?
- এটি সহজ সিনট্যাক্স 😁
- এটি প্রোপার্ট এবং মেথড এর জন্য একই সিনট্যাক্স লিখতে দেয়।
- এটি আরও ভাল ডেটা গুণমান সুরক্ষিত করতে পারে।
- এটি প্রাইভেট প্রোপার্টি নিয়ে কাজ করার জন্য দরকারী
Static Method
এটি ও একটি ফাংশন কিন্তু এটা ইনিশিয়েট হয় Class
এর মধ্যে উপরের কোড অনুসারে Person
ক্লাস এ সেট হবে। চলুন static
দিয়ে একটা ফাংশন তৈরি করি।
class Person { constructor(name, age) { this.name = name; this.age = age; } eat() { console.log(`${this.name} is eating`); } static isEqualAge() { console.log('I am static equation'); } } const sakib = new Person('Sakib', 34); console.log(Person.isEqualAge()); //output - I am static equation
অর্থাৎ static
মেথড গুলো আমাদের অব্জেক্ট এর সাথে কোনো সংযোগ নাই এটা কমপ্লিটলি আলাদা। কিন্তু প্যারামিটার আকারে আমরা আবার আমাদের তৈরি করা অব্জেক্ট কে পাঠাইতে পারি।
class Person { constructor(name, age) { this.name = name; this.age = age; } eat() { console.log(`${this.name} is eating`); } static isEqualAge(cricketer1) { return cricketer1.age; } } const sakib = new Person('Sakib', 34); console.log(Person.isEqualAge(sakib)); //output - 34
আমরা যদি চেষ্ট্রা করি যে isEqualAge
এর মধ্যে যদি this.name
দেই তাহলে কি আমার name
পাবো? নাহ তখন আমরা পাবো Person
কে। তাই বলা যায় static
একটি সম্পূর্ন আলাদা একটি ফাংশন। যাকে ইনবোক করতে হলে Person.something()
করতে হবে।
Polymorphism
এখানে জাভাস্ক্রিপ্ট Class
এ Polymorphism বলতে আর তেমন কিছুই না। একটি ফিচার বলা যায়। চলুন একটা Class
Constructor
দিয়ে একটা অব্জেক্ট ব্লু প্রিন্ট তৈরি করা যাক।
class Person { constructor(name, age) { this.name = name; this.age = age; } play() { console.log(`${this.name} is playing`); } } class Cricketer extends Person { constructor(name, age, type, country) { super(name, age); this.name = name; this.age = age; this.type = type; this.country = country; } } const sakib = new Cricketer('Sakib', 34, 'Cricketer', 'Bangladesh'); sakib.play(); //output - sakib is playing
ফিচারটা হলো Person
ক্লাস এ যে play()
মেথড আছে এটাকে আমরা চাচ্ছি Cricketer
এ একটি মডিফাই ভাবে ব্যবহার করতে। এখানে পেরেন্ট এর মেথড গুলোকে চাইল্ড এ মডিফাই ভাবে ব্যবহার করা কে Polymorphism বলা হয়।
class Person { constructor(name, age) { this.name = name; this.age = age; } play() { console.log(`${this.name} is playing`); } } class Cricketer extends Person { constructor(name, age, type, country) { super(name, age); this.name = name; this.age = age; this.type = type; this.country = country; } play() { console.log(`${this.name} is playing cricket`); } } const sakib = new Cricketer('Sakib', 34, 'Cricketer', 'Bangladesh'); sakib.play(); //output - sakib is playing cricket
কিন্তু এখানে একটি প্রব্লেম হয়ে যায় আমাদের Person
এর play()
মেথডটাও অভাররাইড হয়ে যায়। এখন আমরা দেখবে কিভাবে Person
এবং Cricketer
দুইটা দুই ভাবে হ্যান্ডেল করা যায় মানে কিভাবে দুইটা ফাংশন কেই রাখা যায়। তাহলে এমন কিছুই না জাস্ট প্রথম এ পেরেন্ট থেকে চাইল্ড ফাংশন এ super.play()
কল করে দিলেই হবে।
class Person { constructor(name, age) { this.name = name; this.age = age; } play() { console.log(`${this.name} is playing`); } } class Cricketer extends Person { constructor(name, age, type, country) { super(name, age); this.name = name; this.age = age; this.type = type; this.country = country; } play() { super.play(); console.log(`${this.name} is playing cricket`); } } const sakib = new Cricketer('Sakib', 34, 'Cricketer', 'Bangladesh'); sakib.play(); //output - sakib is playing //output - sakib is plaing cricket
আউটপুট দুইটাই আসবে।
Polymorphism হলো একটা কন্সেপ্ট বা ফিচার বলা যায় যার মাধ্যমে যদি আমার কোনো থার্ড পার্টি প্যাকেজ ব্যবহার করি এবং তার থেকে এক্সটেন্ড করে আমরা কোনো ফাংশন কে মডিফাই করে ব্যবহার করতে চাই তখন এই Polymorphism কন্সেপ্ট এর মাধ্যমে জাভাস্ক্রিপ্ট কাজটি করে থাকে।