Mohammad Bohluli
Mohammad Bohluli
خواندن ۷ دقیقه·۶ ماه پیش

شی گرایی قسمت دوم (deep dive prototype)

توی شی‌گرایی کلاسیک خب طبیعتا ما یک class داریم و هروقت از روی اون کلاس، object بسازیم میگیم عمل instantiation یا همون نمونه سازی اولیه رو انجام دادیم اما توی جاواسکریپت یه کوچولو جریان فرق داره🙃

قبل شروع اینو در نظر داشته باشه که توی جاواسکریپت همه چی یک object هست حتی آرایه، حتی توابع و ...

توی جاواسکریپت یه آبجکتی هست به اسم prototype و هر object ما یک پروتوتایپ داره که کامل توی این مقاله بهش میپردازیم

☝️اگه این پروتوتایپ(خیلی مهمه) رو قشنگ درک کنید قول میدم شی‌گرایی جاواسکریپت براتون عین آب خوردن بشه و شاید این مبحث برای بار اول که بخونید گیج کننده باشه.

این پروتوتایپ قصه ما شامل method و property هایی است که object هایی که به اون پروتوتایپ متصل شده اند می تونن به اون دسترسی داشته باشند و از اونا استفاده کنند

یا به صورت عامیانه آقای x یک جعبه ابزار داره که پر از ابزار الاتی مثل پیچ گوشتی، آچار، چکش و ... اینا هست، اینجا آقای x که یک تعمیر کاره حکم object رو داره اون جعبه ابزاری که همیشه همراهشه حکم prototype رو داره و اون ابزار آلات حکم method و property رو دارن پس هرکس بخواد از اون متد ها و پراپرتی ها(ابزار الات) استفاده کنه کافیه پروتوتایپش رو (جعبه ابزار) رو به اشتراک بزاره

به این اشتراک گذاشتن چی میگن؟ prototype inheritance (ارث بری پروتوتایپ)، در واقع object ما نماینده(delegation) اون پروتوتایپ هستش

نکته: ببینید توی بقیه زبان های برنامه نویسی وقتی یک کلاس از یک کلاس دیگه ارث بری میکنه تمام متد های کلاس والد توی کلاس فرزند کپی میشن ولی اینجا این اتفاق نمیوفته و کپی رخ نمیده بلکه delegation رخ میده، یعنی چی؟ یعنی object ما یک نماینده یا یک وکیل(delegate) از prototype هستش که شامل همه متد ها و پراپرتی های اون پروتوتایپ هست.

میخوام که این نتیجه رو بگیریم این ارث بری با اون ارث بری که مقاله قبل گفتیم فرق داره ها حواستون باشه، توی اونجا دوتا کلاس از هم ارث بری میکردن ولی این جا یک آبجکت داره از کلاس ارث بری میکنه

همون طور که توی عکس میبینید

وقتی شما از یک متد آرایه استفاده میکنید باید از prototype inheritance تشکر کنید، چون اونه که باعث میشه شما به اون متد های آرایه دسترسی داشته باشید
وقتی شما از یک متد آرایه استفاده میکنید باید از prototype inheritance تشکر کنید، چون اونه که باعث میشه شما به اون متد های آرایه دسترسی داشته باشید

پس وقتی توی داکیومنت نوشته Array.prototype.map این قسمتش یعنی Array.prototype گویای اینه که هررر نوع آبجکتی که جنسش از آرایه باشه بتونه به اون متد ها دسترسی داشته باشه (همون مثال آقای x)

اگه برید توی کنسول مرورگرتون Array.prototype رو بزنید کل متد های آرایه رو بهتون نشون میده و این متد ها توی prototype property هستن نه توی Array

یه مثال بزنیم:

وقتی تو اینو تعریف میکنی const numbers = [1,2,3] یعنی داری به جاواسکریپت میگی numbers یه نوع آرایه اس یعنی میتونه به تمام متد های آرایه دسترسی داشته باشه پس یعنی متدی مثل map توی numbers تعریف نشده بلکه توی Array.prototype تعریف شده و numbers به عنوان نماینده میتونه به اون متد ها دسترسی پیدا کنه (به وسیله __proto__ که جلوتر میگمش)



خب به صورت کلی ما به سه شکل میتونیم شی گرایی رو توی جاواسکریپت پیاده سازی کنیم:

1️⃣ شکل اول: constructor function

این تکنیک شی مورد نظرمون رو از یک تابع میسازه، در واقع آبجکت هایی مثل Array و Set و... به همین شکل ساخته شدن

2️⃣ شکل دوم: ES6 Classes

این نوع تکنیک مدرن شده شکل اول هستش و مثل یک پوسته روی شکل اول قرار گرفته یعنی درسته که از کلمات کلیدی class یا constructor استفاده میکنید ولی در پس قضیه همون شکل اول داره رخ میده

3️⃣ شکل سوم: ()Object.create

شاید بشه گفت ساده ترین و سر راست ترین راه لینک کردن یک آبجکت به prototype این راه باشه



خب بریم سر وقت شکل اول یعنی constructor function (تابع سازنده)

تابع سازنده هیچ چیز عجیبی نیست بلکه فقط یه تابع است مثل بقیه توابع با این تفاوت که با کلمه کلیدی new صداش میزنیم

☝️نکته: همیشه اسم تابع سازنده اتون رو با حروف بزرگ بنویسید این یه قرار داد هست مثل همون Array که جاواسکریپت هم از این قرارداد پیروی میکنه
☝️نکته: برای ساخت تابع سازنده به هیچ وجه از arrow function ها استفاده نکنید چون this خودشون رو ندارن توی این مقاله راجع بهش نوشتم
☝️نکته: بلافاصله بعد از ساخت ابجکت از تابع سازنده مورد نظر اتوماتیک خودش صدا زده میشه
☝️نکته: وقتی میگیم تابع سازنده انگار همون کلمه class رو که توی بقیه زبان ها هست داریم به کار میبریم، درواقع شبیه سازی شده class عه

با یه مثال شروع میکنم، تابع سازنده زیر(که در واقع انگار همون class هست) قراره یک Person باشه و اسم و سن شخص توش ذخیره بشه، پس برای اینکه یه نمونه یا شی ازش بسازیم باید از new استفاده کنیم

example constructor
example constructor

حالا وقتی از کلمه new استفاده میکنید پس قضیه چهار اتفاق میوفته:

1️⃣ اول: وقتی خط 6 رو مینویسید یک شی خالی به این صورت { } از Person ساخته میشه

2️⃣ دوم: تابع صدا زده میشه و this به اون آبجکت ساخته شده خالی اختصاص داده میشه یعنی { } = this و اون پراپرتی های firstName و age براش set میشن

3️⃣ سوم: حالا آبجکت { } ما لینک میشه به prototype یعنی proto__ : Person.prototype__

4️⃣ چهارم: و در آخر شی که مرحله اول ساخته شده بود توسط تابع سازنده return میشه وشما قرار نیست چیزی رو return کنید (گفتم که خودش اتوماتیک اجرا میشه)

دقیقا تابع سازنده ما نقش همون طرح ماشینی هست که توی مقاله قبلی راجع بهش حرف زدم، یعنی میتونیم از تابع سازنده امون کلی آبجکت متفاوت بسازیم مثل ali ، Mohammad ، sara و ....

حالا اگه از متد instanceOf استفاده کنید میبینید که ali نمونه ای از Person هست یعنی

console.log(ali instanceOf Person); 👉 true

خب تا اینجا ما برای کلاسمون پراپرتی ساختیم اما اگه بخواییم method بسازیم چطور میشه ؟

حتما پیش خودت میگی خب مثل عکس زیر متد میسازیم دیگه،

wrong method
wrong method

اما سخت در اشتباهی هیچ وقت توی تابع سازنده ات method نساز، میگی چرا ؟

فرض کن از Person سیصد تا آبجکت ساختی، اینطوری عین سیصد تا آبجکتت شامل تمام اون متد ها هستن یعنی انگاری تو با سیصد تا ابجکت، سیصد بار اون همههه متد رو کپی کردی(نه فقط اون هایی رو که نیاز داری)، خب این حرکت پرفورمنس رو نابود میکنه، پس چیکار کنیم؟ اینجاست که باید از prototype و prototype inheritance استفاده کنیم

خب همون طور که بالاتر گفتیم هر تابع سازنده یک پراپرتی داره به اسم prototype پس اگه بیاییم متد هامونو توی اون بسازیم چی میشه ؟😉

در واقع تو با تعریف کردن متد هات توی پراپرتیه prototype یه ظرف میسازی که هر آبجکتی که از Person ساخته شد دست کنه تو اون طرف متد مورد نظرشو برداره استفاده کنه پس یعنی دیگه سیصد بار کپی نمیشه و هر آبجکت بنا به نیازش اون متد رو فراخونی میکنه

یعنی اینطوری :

حالا اگه کد زیر رو اجرا کنی عکس زیر مواجه میشی

console.log(Person.prototype)

میبینی متد sayName جز خود ابجکت نیست و وارد پروتوتایپ شده چون اون جا تعریف شده


ما یک چیز دیگه ای به اسم __proto__ داریم که دقیقا همون prototype هست ولی برای آبجکت ها استفاده میشه پس اگه کد زیر رو بنویسیم باید خروجی true برگردونه

ali.__proto__ === Person.prototype 👉 true

در واقع توی اون مرحله سوم بود(برو یه نگاه بهش بنداز) که __proto__ تعریف میشه و اشاره میکنه به Person.protoype ، اوکی؟😉

شما میتونید علاوه بر متد ها، پراپرتی ها رو از طریق Person.prototype اعلان کنید ولی دقت کنید که دیگه جز پراپرتی های مستقیم آبجکت نیستن و میرن زیر مجموعه __proto__ (دقیقا مثل sayName)و اونایی که جز پراپرتی های مستقیم آبجکتمون هستن، که توسط خود تابع سازنده تعریف بشن مثل همون age و firstName خود جاواسکریپت از طریق متد زیر میفهمه که چی جز چیه

توی مثال بالا چون lastName رو توی Person.prototypeتعریف کردیم پس جز پراپرتی های خود آبجکت نیست

امیدوارم لذت برده باشید ❤️ مخلصیم

کانال تلگرامم LearnByLearn@

خلاصه ای از تمام چیزایی که گفتیم رو هم میتونید توی عکس زیر ببینید(منبع عکس از PDF های جوناس)



برنامه نویسیجاواسکریپت
علاقمند به تکونولوژی و هرچی که بهش مربوطه، کانال تلگرام LearnByLearn@
شاید از این پست‌ها خوشتان بیاید