بررسی Prototype در زبان JavaScript

از آنجایی که زبان JavaScript یک زبان پویا و یا dynamic است به راحتی می توانید property های جدید را در هر زمان که بخواهید به یک object متصل کنید. این موضوع در کد زیر نشان داده شده است.function Student() {this.name = 'John';this.gender = 'Male';}var studObj1 = new Student();studObj1.age = 15;alert(studObj1.age); // 15var studObj2 = new Student();alert(studObj2.age); // undefinedهمانطور که در مثال بالا مشاهده می کنید یک property به نام age به یک instance و یا object به اسم studObj1 اضافه شده است؛ اما studObj2 پروپرتی age را در خود نخواهد داشت؛ چرا که این property فقط بر روی شیء studObj1 تعریف شده است. بنابراین، اگر بخواهیم property های جدید را در ادامه کار برنامه به یک function و یا تابع اضافه کنیم که از آن instance های مختلف ساخته شده است چه باید کرد؟به عبارت دیگر، اگر بخواهیم تمامی instance ها این property را دریافت کنند روال انجام کار چیست؟ پاسخ این سوال استفاده کردن از prototype است.prototype در زبان JavaScript یک object است که با توابع و object ها مرتبط می‌شود. نکته مهم در رابطه با prototype این است که پروپرتی prototype مربوط به یک function هم قابل تغییر کردن و هم قابل دسترسی است؛ اما پروپرتی prototype مربوط به یک object که تحت نام attribute نیز شناخته می شود قابل دیدن نمی باشد.هر تابع شامل شیء prototype به صورت پیش فرض می باشد. این موضوع در تصویر زیر نشان داده شده است.شیء prototype از یک type و یا نوع داده ای خاص است. این نوع داده ای enumerable می باشد. بنابراین، می‌توان property های جدید را به آن اضافه کرد و در بین تمامی instance ها و یا object های ایجاد شده از یک constructor function مشترکاً استفاده کرد.از پروپرتی prototype مربوط به یک function در مثال بالا می توان برای اضافه کردن پروپرتی age به تمامی object های ایجاد شده از این function استفاده نمود. این موضوع در کد زیر نشان داده شده است.function Student() {this.name = 'John';this.gender = 'M';}Student.prototype.age = 15;var studObj1 = new Student();alert(studObj1.age); // 15var studObj2 = new Student();alert(studObj2.age); // 15هر object که توسط یک constructor syntax و یا literal syntax ایجاد شده باشد، شامل پروپرتی __proto__ می باشد که به شیء prototype مربوط به function که آن object را ایجاد کرده است اشاره می‌کند. این موضوع در تصویر زیر نشان داده شده است.می‌توان به سادگی برنامه را debug کرد و پروپرتی prototype مربوط به توابع و یا object ها را در developer tools مربوط به مرورگر دید. این موضوع در کد زیر نشان داده شده است.function Student() {this.name = 'John';this.gender = 'M';}var studObj = new Student();console.log(Student.prototype); // objectconsole.log(studObj.prototype); // undefinedconsole.log(studObj.__proto__); // objectconsole.log(typeof Student.prototype); // objectconsole.log(typeof studObj.__proto__); // objectconsole.log(Student.prototype === studObj.__proto__ ); // trueهمانطور که در مثال بالا مشاهده می کنید prototype مربوط به function می‌تواند با استفاده از کلمه prototype مورد دسترسی قرار بگیرد؛ اما یک object پروپرتی prototype خود را در اختیار ما قرار نمی گذارد که در این صورت می توان به عنوان جایگزین از __proto__ استفاده کرد.دقت کنید که پروپرتی prototype یک نوع داده ای و یا type خاص است که به صورت enumerable تعریف شده است؛ اما نمی توان با استفاده از حلقه های تکرار شبیه for و foreach آن را پیمایش کرد.بررسی پروپرتی Prototype مربوط به یک Objectهمانطور که در بالا گفتیم رو پروپرتی prototype مربوط به یک object غیر پدیدار و یا اصطلاحاً invisible می باشد. می توان از Object.getPrototypeOf(obj) به جای __proto__ به منظور دسترسی پیدا کردن به شیء prototype استفاده نمود. این موضوع در کد زیر نشان داده شده است.function Student() {this.name = 'John';this.gender = 'M';}var studObj = new Student();Student.prototype.sayHi= function(){alert("Hi");};var studObj1 = new Student();var proto = Object.getPrototypeOf(studObj1); // returns Student's prototype objectalert(proto.constructor); // returns Student functionتغییر دادن Prototypeهمانطور که در قسمت بالا گفتیم prototype مربوط به یک object به prototype مربوط به یک تابع مرتبط می‌شود. بنابراین، اگر prototype مربوط به یک تابع را تغییر دهید، سپس object های جدیدی که ایجاد می شوند به آن prototype تغییر یافته مرتبط می‌شوند؛ اما تمامی object هایی که از قبل ایجاد شده اند؛ همچنین به prototype قدیمی مربوط به آن تابع متصل هستند. مثالی که در قسمت زیر مشاهده می کنید این موضوع را نشان می‌دهد.function Student() {this.name = 'John';this.gender = 'M';}Student.prototype.age = 15;var studObj1 = new Student();alert('studObj1.age = ' + studObj1.age); // 15var studObj2 = new Student();alert('studObj2.age = ' + studObj2.age); // 15Student.prototype = { age : 20 };var studObj3 = new Student();alert('studObj3.age = ' + studObj3.age); // 20alert('studObj1.age = ' + studObj1.age); // 15alert('studObj2.age = ' + studObj2.age); // 15کاربردها و استفاده کردن از prototypeشیء prototype می تواند توسط موتور JavaScript به دو شکل مورد استفاده قرار بگیرد.به منظور پیدا کردن property ها و متدهای مربوط به یک objectبه منظور پیاده‌سازی وراثت در JavaScriptاین موضوع در کد زیر نشان داده شده است.function Student() {this.name = 'John';this.gender = 'M';}Student.prototype.sayHi = function(){alert("Hi");};var studObj = new Student();studObj.toString();همانطور که در مثال بالا مشاهده می کنید متد toString در Student تعریف نشده است. پس چگونه توانسته است از این متد استفاده کند؟ در این قسمت prototype وارد عمل می‌شود. در ابتدا موتور JavaScript چک می کند که آیا متد toString به studObj اضافه شده است یا نه؟دقت کنید که اضافه کردن یک function جدید به یک instance و یا object در JavaScript امکان پذیر است. اگر موتور JavaScript نتواند این متد را پیدا کند از __proto__ مربوط به studObj استفاده می‌کند. این property به prototype مربوط به تابع Student اشاره می کند. اگر هنوز هم نتواند متد toString را پیدا کند، به یک مرحله بالاتر از سلسله مراتب وراثت رفته و چک می کند که آیا prototype بر روی تابع object تعریف شده است یا نه؛ چرا که در زبان JavaScript تمامی object ها از Object ارث بری می کند.بنابراین متد toString می تواند در prototype مربوط به تابع Object پیدا شود و بر روی studObj صدا زده شود. به این ترتیب prototype می‌تواند به منظور نگهداری فقط یک کپی از توابع در تمامی object ها مورد استفاده قرار بگیرد. تصویر زیر جزئیات مربوط به این سناریو را نشان می دهد.منبع: وبسایت پرووید