Prototype در جاوا اسکریپت چیست؟ – توضیح به زبان ساده + مثال و کد
لازمه یادگیری زبان برنامه نویسی جاوا اسکریپت آشنایی خوب و عمیق با مفهوم شی در برنامه نویسی است. تسلط بر مفهوم «اشیا» (Objects) و مباحث پیرامون آن به عنوان یکی از اجزای اساسی زبانهای برنامه نویسی شی گرا محسوب میشود که جاوا اسکریپت نیز از این قاعده مستثنی نیست. در این مطلب از مجله فرادرس پیرامون نمونههای اولیه یا Prototype در جاوا اسکریپت بحث شده است تا مفهوم اشیا در جاوا اسکریپت و نحوه تعامل با آنها را بهتر درک کنیم. پیش از پرداختن به بحث Prototype در جاوا اسکریپت، آشنایی با مفهوم اشیا یا شی در جاوا اسکریپت خالی از لطف نیست.
اشیا در جاوا اسکریپت
در جاوا اسکریپت، شی نوعی ساختار داده است که به کاربر امکان میدهد دادهها را در جفتهای «کلید-مقدار» (Key-Value) ذخیره و سازماندهی کند. شی یکی از انواع دادههای اساسی در جاوا اسکریپت به حساب میآید و مجموعهای از ویژگیها را در بر میگیرد که هر ویژگی از کلیدی (که نام ویژگی نیز نامیده میشود) و مقدارش تشکیل شده است.
اشیاء در جاوا اسکریپت پویا هستند، به این معنی که کاربر میتواند در هر زمان ویژگیها را تغییر دهد یا آنها را اضافه یا حذف کند. مقادیر ویژگیهای شی میتوانند از هر نوع دادهای باشند، از جمله اشیای دیگر، میتوان آرایهها، توابع و انواع اولیه مانند اعداد و «رشتهها» (استرینگ) را نام برد. مثال زیر مفهوم ایجاد شی در جاوا اسکریپت را بیان میکند.
1const car = {
2 brand: "Tesla",
3 model: "Model S",
4 getInfo: function() {
5 console.log(`Brand: ${this.brand}, Model: ${this.model}`);
6 }
7};
8
9const bike = {
10 brand: "Honda",
11 model: "CBR 1000RR"
12};
13
14// Using bind to bind the car's getInfo method to the bike object
15const boundGetInfo = car.getInfo.bind(bike);
16
17boundGetInfo(); // Output: Brand: Honda, Model: CBR 1000RR
تصویر زیر نحوه ساخت شی در جاوا اسکریپت را نشان میدهد:
اگر فقط یک شی وجود داشته باشد، روند بالا ساده خواهد بود. اما در برخی از سناریوها، لازم است چندین چند شی مختلف ایجاد شود که این کار روند را کمی پیچیدهتر میکند. منطقیترین روش برای چنین سناریوهایی این است که منطق ایجاد شی در تابعی قرار بگیرد. با فراخوانی این تابع، میتوان در صورت نیاز شیئی جدید را ایجاد کرد. از این الگو به عنوان «نمونهسازی عملکردی یا کاربردی» (Functional Instantiation) یاد میکنند و تابع درگیر به عنوان «عملکرد سازنده» (Constructor Function) شناخته میشود زیرا اساساً شیئی جدید را میسازد.
نمونه سازی عملکردی
یکی از پیشنیازهای اصلی Prototype در جاوا اسکریپت درک مفهوم نمونهسازی عملکردی است.
برای درک مفهوم نمونه سازی عملکردی، فرض میشود که قطعه کد زیر را داریم. کدهای زیر حاوی تابعی برای تعریف حیوانات مختلف با ویژگی نام و انرژی است.
1function Animal (name, energy) {
2 let animal = {}
3 animal.name = name
4 animal.energy = energy
5
6 animal.eat = function (amount) {
7 console.log(`${this.name} is eating.`)
8 this.energy += amount
9 }
10
11 animal.sleep = function (length) {
12 console.log(`${this.name} is sleeping.`)
13 this.energy += length
14 }
15
16 animal.play = function (length) {
17 console.log(`${this.name} is playing.`)
18 this.energy -= length
19 }
20
21 return animal
22}
23
24const leo = Animal('Leo', 7)
25const snoop = Animal('Snoop', 10)
در کدهای بالا، هر زمان که حیوان جدیدی ایجاد شود، باید هر بار متدهای عمومی (eat و sleep ،play) را بازسازی کرد. این روش کارآمد نیست و حافظه غیر ضروری را مصرف خواهد کرد. برای حل این مشکل، میتوان الگویی به نام «Functional Instantiation with Shared Methods» اتخاذ کرد. این الگو شامل انتقال متدهای عمومی به شیئی جداگانه و اجازه دادن به هر حیوانی برای ارجاع به آن شی است. با انجام این کار، میتوان از بازآفرینی متدها برای هر حیوان جلوگیری کرد و کدها را با حافظه بهینهتری نوشت.
نمونه سازی عملکردی با متدهای مشترک
به منظور پرداختن به مسئله زیادهروی در مصرف حافظه، الگوی نمونهسازی کاربردی با متدهای مشترک در کدهای زیر پیادهسازی شدهاند.
1const animalMethods = {
2 eat(amount) {
3 console.log(`${this.name} is eating.`)
4 this.energy += amount
5 },
6 sleep(length) {
7 console.log(`${this.name} is sleeping.`)
8 this.energy += length
9 },
10 play(length) {
11 console.log(`${this.name} is playing.`)
12 this.energy -= length
13 }
14}
15
16function Animal (name, energy) {
17 let animal = {}
18 animal.name = name
19 animal.energy = energy
20 animal.eat = animalMethods.eat
21 animal.sleep = animalMethods.sleep
22 animal.play = animalMethods.play
23
24 return animal
25}
26
27const leo = Animal('Leo', 7)
28const snoop = Animal('Snoop', 10)
در کدهای بالا، شیئی جداگانه به نام animalMethods تعریف شده است که شامل متد عمومی (eat,sleep,play)باشد. در تابع Animal ، یک شی حیوان جدید ایجاد و نام و خواص انرژی به آن داده شده است. به جای بازآفرینی متدها برای هر حیوان، اکنون با متدهای مشترک از شی animalMethods کار انجام گرفته است. این رویکرد تضمین میکند که متدها تکراری نمیشوند و در نتیجه کدها با حافظه کارآمدتری ایجاد میشوند. با اتخاذ این الگو، مشکل اتلاف حافظه با موفقیت حل خواهد شد و اندازه اشیا کاهش داده میشود.
object.create در جاوا اسکریپت چیست؟
object.create نوعی متد جاوا اسکریپت داخلی است که به کاربر امکان میدهد شیئی جدید ایجاد و شی دیگری را به عنوان نمونه اولیه آن تنظیم کند. هنگامی که ویژگی خاصی در شی جدید یافت نمیشود، جاوا اسکریپت سعی میکند آن را در نمونه اولیه خود جستجو کند. به این رفتار، «تفویض» (Delegation) میگویند. بیایید این مفهوم را با مثالی توضیح دهیم.
فرض بر این است که شیئی والد به صورت زیر وجود دارد.
1const parent = {
2 name: 'Stacey',
3 age: 35,
4 heritage: 'Irish'
5}
میتوان شی child با parent به عنوان نمونه اولیه آن به صورت زیر ایجاد کرد.
1const child = Object.create(parent);
2child.name = 'Ryan';
3child.age = 7;
حال، اگر کاربری بخواهد به ویژگیهای شی child دسترسی پیدا کند، باید طبق قطعه کد زیر عمل شود.
1console.log(child.name) // Ryan
2console.log(child.age) // 7
3console.log(child.heritage) // Irish
حتی اگر ویژگی heritage مستقیماً روی شی child تعریف نشده باشد، جاوا اسکریپت همچنان میتواند آن را در شی parent پیدا کند که نمونه اولیه شی child است. بنابراین چگونه میتوان از Object.create برای ساده کردن کد Animalقبلی استفاده کرد؟ با این اوصاف میتوان از Object.create برای به اشتراک گذاشتن متدها بین نمونههای حیوانی مختلف استفاده کرد، نه اینکه این متدها به هر حیوان جداگانه اضافه شوند. این رویکرد را میتوان به عنوان نمونهسازی کاربردی یا عملکردی با متدهای مشترک به وسیله Object.create نامید.
نمونه سازی کاربردی با متدهای مشترک و Object.create
متد Object.createابزاری مفید در جاوا اسکریپت به خساب میآید که به کاربر امکان میدهد شیئی را ایجاد کرده و جستجوهای ویژگی را به شیئی دیگر واگذار کند. این مفهوم به وسیله مثال مربوط به حیوانات به خوبی نشان داده شده است.
برای درک بهتر، بلوک کد زیر ارائه شده است که متدهای رایج برای حیوانات را تعریف میکند.
1const animalMethods = {
2 eat(amount) {
3 console.log(`${this.name} is eating.`)
4 this.energy += amount
5 },
6 sleep(length) {
7 console.log(`${this.name} is sleeping.`)
8 this.energy += length
9 },
10 play(length) {
11 console.log(`${this.name} is playing.`)
12 this.energy -= length
13 }
14}
سپس میتوان تابع Animalرا برای ایجاد اشیای حیوانی که به animalMethods واگذار میشوند، به صورت زیر تعریف کرد:
1function Animal (name, energy) {
2 let animal = Object.create(animalMethods)
3 animal.name = name
4 animal.energy = energy
5
6
7 return animal
8}
9
10
11const leo = Animal('Leo', 7)
12const snoop = Animal('Snoop', 10)
13
14
15leo.eat(10)
16snoop.play(5)
در این مورد، وقتی leo.eat فراخوانی میشود، جاوا اسکریپت ابتدا بررسی میکند که eat در leo وجود دارد یا خیر و از آنجایی که leoاین متد را ندارد، جاوا اسکریپت سپس به animalMethods نگاه میکند. این به دلیل تفویض اختیاری اتفاق میافتد که از راه Object.createایجاد شده است.
با این حال، داشتن نوعی شی animalMethodsمجزا برای نگهداری متدهای مشترک، ممکن است چندان هم حرفهای به نظر نرسد. امکان دارد کاربری فکر کند این نوع به اشتراکگذاری متُد باید نوعی ویژگی باشد که در زبان تعبیه شده است. در واقع جاوا اسکریپت دارای چنین ویژگی است که به آن نمونه اولیه یا Prototype در جاوا اسکریپت میگویند. هر تابع در جاوا اسکریپت دارای یک ویژگی به نام نمونه اولیه یا Prototype است که به شی اشاره میکند. مثال زیر این مفهوم را بیان میکند:
1function doThing () {}
2console.log(doThing.prototype) // {}
اگر متدهای مشترک مستقیماً روی نمونه اولیه Animal قرار داده شوند به این ترتیب، به جای تفویض اختیار به animalMethods، به Animal.prototype تفویض انجام خواهد شد. این الگو به عنوان Prototype در جاوا اسکریپت شناخته شده است.
Prototype در جاوا اسکریپت چیست؟
prototype جاوا اسکریپت ساز و کاری است که به وسیله آن، اشیا ویژگیهایی را از یکدیگر به ارث میبرند. به این ساز و کار، وراثت نمونه اولیه یا Prototype گفته میشود. هنگامی که تابعی در جاوا اسکریپت ایجاد میشود، موتور جاوا اسکریپت ویژگی Prototype را به تابع اضافه میکند. این ویژگی، نمونه اولیه نوعی شی به نام شی نمونه اولیه محسوب میشود که به طور پیشفرض دارای ویژگی سازنده است. ویژگی سازنده به تابعی اشاره میکند که در آن شی نمونه اولیه نوعی ویژگی است. میتوان به وسیله «FunctionName.prototype» به ویژگی نمونه اولیه تابع دسترسی پیدا کرد.
تمامی مباحث بالا، نوعی پیشنیاز برای Prototype در جاوا اسکریپت بودند. حال مفهوم Prototype در قالب مثالی ساده در این جا توضیح داده خواهد شد. فرض بر این است که تابعی به نام Animalبه صورت زیر وجود دارد: