This translation is incomplete. Please help translate this article from English.
যেসব ডেভেলপার আগে ক্লাস-ভিত্তিক প্রোগ্রামিং ল্যাংগুয়েজে (যেমন সি++ বা জাভা) কোড করছেন, তারা জাভাস্ক্রিপ্টে কোড করতে গিয়ে একটু বিভ্রান্ত হবেন, কেননা জাভাস্ক্রিপ্ট একটি ডায়নামিক ল্যাংগুয়েজ আর এতে কোন class
ইমপ্লিমেন্টেশন নেই ( যদিও class
নামে একটি কীওয়ার্ড জাভাস্ক্রিপ্ট সংরক্ষণ করে রেখেছে - তাই এই নামে কোন ভ্যারিয়েবল তৈরি করতে পারবেন না )।
জাভাস্ক্রিপ্টে ইনহেরিটেন্স বিষয়ক একটি মাত্র কন্সট্রাক্ট আছেঃ অবজেক্ট। সব অবজেক্ট এরই আরেকটা অবজেক্ট এর সাথে অভ্যন্তরীণ লিঙ্ক থাকে যাকে তার প্রটোটাইপ বলে। আবার এই প্রোটাটাইপ অবজেক্টের ও নিজস্ব একটি প্রটোটাইপ থাকে। এভাবে চলতেই থাকে, যতক্ষণ না আমরা null
হিসেবে কোন অবজেক্টের প্রটোটাইপ পাই। null
এর কোন প্রটোটাইপ থাকে না, তাই যখন null পাব তখন বুঝতে হবে প্রটোটাইপ চেইনের শেষপ্রান্তে এসে গেছি।
প্রটোটাইপ মডেল কে অনেকে জাভাস্ক্রিপ্টের দুর্বলতা বললেও আসলে প্রটোটাইপ-ভিত্তিক ইনহেরিটেন্স মডেল সি++/জাভা ভিত্তিক ক্লাসিকাল মডেলের থেকে অনেক শক্তিশালী। যেমন, প্রটোটাইপ মডেল কে ভিত্তি করে আমরা সহজেই ক্ল্যাসিকাল মডেল তৈরি করতে পারি, কিন্তু ক্লাসিকাল মডেলের ওপর ভিত্তি করে চাইলেই প্রটোটাইপ ভিত্তিক মডেল তৈরি করা সম্ভব না।
প্রটোটাইপ-চেইন ভিত্তিক ইনহেরিটেন্স
প্রোপার্টি ইনহেরিট করা
জাভাস্ক্রিপ্টে অবজেক্ট কে আমরা বিভিন্ন প্রোপার্টির "ব্যাগ" (থলে) হিসেবে কল্পনা করতে পারিঃ এই ব্যাগে যেকোন সময় যেকোন প্রোপার্টি ঢুকানো যায়, ব্যাগ থেকে ফেলেও দেওয়া যায় (যে কারণে আমরা জাভাস্ক্রিপ্টকে ডায়নামিক বলি) । প্রতিটা অব্জকেটের সাথে একটা বিশেষ অবজেক্টের লিংক থাকে, এই বিশেষ অবজেক্ট টাকে আমরা মূল অবজেক্টটার প্রটোটাইপ বলি। আমরা যখন কোন অবজেক্টের কোন প্রোপার্টি কে ব্যবহার করতে চাই, তখন যা ঘটেঃ
// ধরে নেই, আমার o নামের একটা অবজেক্ট আছে যেটার প্রোটোটাইপ চেইন এমনঃ // {a:1, b:2} ---> {b:3, c:4} ---> null // মানে হল, o অবজেক্ট হচ্ছে {a:1, b:2}, যেটার প্রোটোটাইপ হচ্ছে {b:3, c:4} ইনহেরিটেন্স হিসেবে চিন্তা করলে, // {a:1, b:2} অবজেক্ট টা {b:3, c:4} অবজেক্ট থেকে ইনহেরিট করা হয়েছে। {b:3, c:4} এর কোন প্রোটাটাইপ নাই, যেকারণে null দেখানো হয়েছে। // 'a' আর 'b' এই দুইটা কেবল o অবজেক্টের নিজস্ব প্রোপার্টি। // এই উদাহরণে, someObject.[[Prototype]] দিয়ে আমরা someObject অবজেক্টের প্রোটাটাইপ বুঝিয়েছি। // এইটা শুধুই উদাহরণের জন্য (ECMAScript স্ট্যান্ডার্ড অনুযায়ী) আর আসল কোডে এরকম কিছু লেখা যাবে না! console.log(o.a); // 1 // o অবজেক্টের কি 'a' নামে কোন প্রোপার্টী আছে? হ্যাঁ, আর এর মান হল 1 console.log(o.b); // 2 // o অবজেক্টের কি 'b' নামে কোন প্রোপার্টী আছে? হ্যাঁ, আর এর মান হল 2 // o অবজেক্টের প্রোটোটাইপেরও কিন্তু 'b' প্রোপার্টী ছিল, কিন্তু এটি দেখা যাবে না। একে বলে "property shadowing" console.log(o.c); // 4 // o অবজেক্টের কি 'c' নামে কোন প্রোপার্টী আছে? নাই, তাহলে এর প্রোটোটাইপে দেখতে হবে এই নামে প্রোপার্টি আছে কিনা। // o.[[Prototype]] এ কি 'c' প্রোপার্টি আছে? ? হ্যাঁ, আর এর মান হল 4 console.log(o.d); // undefined // o অবজেক্টের কি 'd' নামে কোন প্রোপার্টী আছে? নাই, তাহলে এর প্রোটোটাইপে দেখতে হবে এই নামে প্রোপার্টি আছে কিনা। // o.[[Prototype]] অবজেক্টের কি 'd' নামে কোন প্রোপার্টী আছে? নাই, তাহলে এর প্রোটোটাইপে দেখতে হবে এই নামে প্রোপার্টি আছে কিনা। // o.[[Prototype]].[[Prototype]] এ null পেয়েছি, তারমানে আর খোজার কিছু নাই। undefined রিটার্ন করতে হবে।
কোন অবজেক্টের প্রোপার্টি সেট করে দিলে সেটা সেই অবজেক্টের নিজস্ব প্রোপার্টি হিসেবে কাজ করে। একমাত্র ব্যতিক্রম হলঃ getter অথবা setter দিয়ে যখন ইনহেরিটেড প্রোপার্টি নিয়ে কাজ করা হয়।
"মেথড" ইনহেরিট করা
ক্লাস-ভিত্তিক প্রোগ্রামিং ভাষাতে যেভাবে "মেথড" বা ফাংশন এর ধারণা প্রচলিত আছে, জাভাস্ক্রিপ্টে মেথডের ধারণা টা ঠিক সেভাবে নয়। জাভাস্ক্রিপ্টে, যেকোন ফাংশন সাধারণ প্রোপার্টির মতই অবজেক্টে যোগ করা যায়। ইনহেরিট করা ফাংশন অবজেক্টের বাদবাকি প্রোপার্টির মতই স্বাভাবিকভাবে কাজ করবে। উপরের উদাহরণে দেখানো property shadowing এখানেও কাজ করবে (এক্ষেত্রে একে বলা হবে method overriding বা ফাংশন ওভাররাইডিং)
যখন ইনহেরিট-হওয়া কোন ফাংশন একজিকিউট হয়, তখন this
এর মান হিসেবে ইনহেরিট-হওয়া অবজেক্ট টা থাকে, প্রোটোটাইপ অবজেক্ট টা না যেখানে ফাংশনটা নিজস্ব প্রোপার্টি হিসেবে দেওয়া হয়েছিল।
var o = { a: 2, m: function(b){ return this.a + 1; } }; console.log(o.m()); // 3 // এখানে o.m কল করার সময়, 'this' পয়েন্ট করে আছে o কে। var p = Object.create(o); // p অবজেক্ট টা o থেকে ইনহেরিট করা হয়েছে p.a = 12; // 'a' নামে p অবজেক্টে নিজস্ব প্রোপার্টি তৈরি হল console.log(p.m()); // 13 // এখানে p.m কল করার সময়, 'this' পয়েন্ট করে আছে p কে। // তাই যখন o অবজেক্টের m ফাংশনটা p ইনহেরিট করল, 'this.a' এর মানে দাঁড়ালো p.a, 'a' নামের p এর নিজস্ব প্রোপার্টি।
অবজেক্ট তৈরি করার বিভিন্ন উপায় এবং ফলাফল হিসেবে প্রোটোটাইপ চেইন
সিন্ট্যাক্স কন্সট্রাক্টস ব্যবহার করে অবজেক্ট তৈরি করা
var o = {a: 1}; // নতুন তৈরি করা o অবজেক্টের প্রটোটাইপ হচ্ছে Object.prototype // o এর 'hasOwnProperty' নামের কোন নিজস্ব প্রোপার্টি নেই। // hasOwnProperty হচ্ছে Object.prototype এর একটি নিজস্ব প্রোপার্টি। তাই o, Object.prototype থেকে hasOwnProperty ইনহেরিট করেছে। // Object.prototype এর প্রোটোটাইপ হচ্ছে null // o ---> Object.prototype ---> null var a = ["yo", "whadup", "?"]; // Arrays inherit from Array.prototype (which has methods like indexOf, forEach, etc.) // The prototype chain looks like: // a ---> Array.prototype ---> Object.prototype ---> null function f(){ return 2; } // Functions inherit from Function.prototype (which has methods like call, bind, etc.) // f ---> Function.prototype ---> Object.prototype ---> null
কন্সট্রাক্টর এর সাহায্যে
জাভাস্ক্রিপ্টে "কন্সট্রাক্টর", "শুধুই" সাধারণ যেকোন ফাংশন যাকে কিনা new অপারেটর দিয়ে কল করা হয়।
function Graph() { this.vertexes = []; this.edges = []; } Graph.prototype = { addVertex: function(v){ this.vertexes.push(v); } }; var g = new Graph(); // g is an object with own properties 'vertexes' and 'edges'. // g.[[Prototype]] is the value of Graph.prototype when new Graph() is executed.
Object.create দিয়ে
ECMAScript 5 নতুন একটি মেথড নিয়ে এসেছেঃ Object.create। এই মেথড কল করলে নতুন একটি অবজেক্ট তৈরি হয় যার প্রোটোটাইপ হল মেথডটির প্রথম প্যারামিটারঃ
var a = {a: 1}; // a ---> Object.prototype ---> null var b = Object.create(a); // b ---> a ---> Object.prototype ---> null console.log(b.a); // 1 (inherited) var c = Object.create(b); // c ---> b ---> a ---> Object.prototype ---> null var d = Object.create(null); // d ---> null console.log(d.hasOwnProperty); // undefined, because d doesn't inherit from Object.prototype
পারদর্শীতা
কোন অবজেক্টে কোন প্রোপার্টি আছে কিনা সেটা জানার জন্য, অবজেক্টের পুরো প্রটোটাইপ-চেইন খুঁজে দেখতে হয়। কোডের পার্ফরমেন্সে এটা ভালই বাজে প্রভাব ফেলে। যেসব কোডের পারফরমেন্স খুব ভাল হওয়া দরকার, সেখানে এই ইস্যু প্রভাব ফেলতে পারে। অবজেক্টে কোন প্রোপার্টি খুঁজে না পেলে পুরো প্রোটোটাইপ চেইনের কোথাও প্রোপার্টি টা আছে কিনা খুঁজা হয়।
এছাড়াও, অবজেক্টের প্রোপার্টি গুলো ঘুরে দেখার সময় (iterate করার সময়) প্রোটোটাইপ-চেইনের প্রত্যেকটা প্রোপার্টি খুঁজে দেখা (enumerate করা) হবে।
অবজেক্টে কোন প্রোপার্টি শুধুমাত্র নিজস্ব প্রোপার্টি হিসেবে আছে কিনা (প্রোটোটাইপ চেইনের অন্য কোথাও নয়) hasOwnProperty
মেথড ব্যবহার উচিত। সব অবজেক্ট এই মেথডটাকে Object.prototype থেকে ইনহেরিট করে।
জাভাস্ক্রিপ্টে hasOwnProperty
-ই একমাত্র ফাংশন যা প্রোপার্টি নিয়ে কাজ করে এবং পুরো প্রোটোটাইপ চেইন ঘুরে (traverse) না।
undefined
কিনা এই চেক করাই যথেষ্ট নয়। হতে পারে প্রোপার্টি টি বহাল তবিয়তেই আছে, কেবল এর মান undefined
দেওয়া হয়েছে।বদ অভ্যাসঃ নেটিভ প্রোটোটাইপ এক্সটেন্ড করা
একটা বাজে ব্যাপার প্রায়ই করা হয় তা হল Object.prototype
অথবা অন্য কোন বিল্ট-ইন প্রটোটাইপ এক্সটেন্ড করা।
এই পদ্ধতিকে বলা হয় monkey patching যা encapsulation এর সর্বনাশ ছাড়া আর কিছুই নয়। Prototype.js এর মত কিছু জনপ্রিয় ফ্রেমওয়ার্কে এর ব্যবহার দেখা গেলেও, স্ট্যান্ডার্ড-নয়, এমন ফিচার দিয়ে বিল্ট-ইন টাইপগুলোর বোঝা বাড়ানোর কোন মানে হয় না।
বিল্ট-ইন টাইপ এক্সটেন্ড করার একমাত্র যৌক্তিক কারণ হতে পারে জাভাস্ক্রিপ্টের নতুন ফিচার গুলো পুরনো ইঞ্জিনে দেওয়া; যেমন Array.forEach
, ইত্যাদি।
পরিশেষে
প্রটোটাইপ ইনহেরিটেন্স মডেল এর ভিত্তি করে জটিল জটিল কোড লেখার আগে একে ভালভাবে বুঝা উচিত। এছাড়াও, পারফরমেন্স জনিত সমস্যা থেকে দূরে থাকার জন্য লম্বা প্রটোটাইপ চেইন পরিহার করতে হবে আর যেখানে সম্ভব প্রটোটাইপ চেইন ভেঙ্গে ছোট করতে হবে। সবশেষে, জাভাস্ক্রিপ্টের নতুন ফিচার যাতে সব ইঞ্জিনেই চলে, শুধুমাত্র সেক্ষেত্রে নেটিভ প্রোটোটাইপ এক্সটেন্ড করা যাবে, এছাড়া এর ব্যবহার সর্বক্ষেত্রে নিরুৎসাহিত করা হয়।