توی شیگرایی کلاسیک خب طبیعتا ما یک class داریم و هروقت از روی اون کلاس، object بسازیم میگیم عمل instantiation یا همون نمونه سازی اولیه رو انجام دادیم اما توی جاواسکریپت یه کوچولو جریان فرق داره🙃
قبل شروع اینو در نظر داشته باشه که توی جاواسکریپت همه چی یک object هست حتی آرایه، حتی توابع و ...
توی جاواسکریپت یه آبجکتی هست به اسم prototype و هر object ما یک پروتوتایپ داره که کامل توی این مقاله بهش میپردازیم
☝️اگه این پروتوتایپ(خیلی مهمه) رو قشنگ درک کنید قول میدم شیگرایی جاواسکریپت براتون عین آب خوردن بشه و شاید این مبحث برای بار اول که بخونید گیج کننده باشه.
این پروتوتایپ قصه ما شامل method و property هایی است که object هایی که به اون پروتوتایپ متصل شده اند می تونن به اون دسترسی داشته باشند و از اونا استفاده کنند
یا به صورت عامیانه آقای x یک جعبه ابزار داره که پر از ابزار الاتی مثل پیچ گوشتی، آچار، چکش و ... اینا هست، اینجا آقای x که یک تعمیر کاره حکم object رو داره اون جعبه ابزاری که همیشه همراهشه حکم prototype رو داره و اون ابزار آلات حکم method و property رو دارن پس هرکس بخواد از اون متد ها و پراپرتی ها(ابزار الات) استفاده کنه کافیه پروتوتایپش رو (جعبه ابزار) رو به اشتراک بزاره
به این اشتراک گذاشتن چی میگن؟ prototype inheritance (ارث بری پروتوتایپ)، در واقع object ما نماینده(delegation) اون پروتوتایپ هستش
نکته: ببینید توی بقیه زبان های برنامه نویسی وقتی یک کلاس از یک کلاس دیگه ارث بری میکنه تمام متد های کلاس والد توی کلاس فرزند کپی میشن ولی اینجا این اتفاق نمیوفته و کپی رخ نمیده بلکه delegation رخ میده، یعنی چی؟ یعنی object ما یک نماینده یا یک وکیل(delegate) از prototype هستش که شامل همه متد ها و پراپرتی های اون پروتوتایپ هست.
میخوام که این نتیجه رو بگیریم این ارث بری با اون ارث بری که مقاله قبل گفتیم فرق داره ها حواستون باشه، توی اونجا دوتا کلاس از هم ارث بری میکردن ولی این جا یک آبجکت داره از کلاس ارث بری میکنه
همون طور که توی عکس میبینید
پس وقتی توی داکیومنت نوشته 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 استفاده کنیم
حالا وقتی از کلمه 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 بسازیم چطور میشه ؟
حتما پیش خودت میگی خب مثل عکس زیر متد میسازیم دیگه،
اما سخت در اشتباهی هیچ وقت توی تابع سازنده ات 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 های جوناس)