برنتی / می‌نویسم که فکر کنم

دنیای جاوااسکریپت ۵ - Prototypes

1399.06.01·دنیای جاوااسکریپت

این مطلب را با یک مثال عجیب شروع می‌کنیم.

let pizza = {};
console.log(pizza.taste); // "pineapple"

خروجی کد زیر "pineapple" است به نظر شما چنین اتفاقی ممکن است؟
ما صرفا یک شیئ pizza ایجاد کردیم که هیچ پراپرتی ندارد و انتظار داریم خروجی مقدار برابر باشد ولی اینگونه نیست. با مفاهیمی که تا بحال آموختیم نمی‌توانیم تصور کنیم که چه کدهایی قبل از این دو خط می‌تواند موجب این اتفاق شود پس مدل ذهنی ما کامل نیست.

برای حل این معما در این مطلب به شرح prototypes می‌پردازیم. و از همه مهمتر اینکه prototypes قلب چندین ویژگی‌ مختلف جاوااسکریپت هستند. معمولا افراد به یادگیری این مفهوم کم توجهی می‌کنند زیرا این مفهوم از نظر آنها خیلی غیر عادی به نظر می‌رسد درصورتی که مفهومی که در آن نهفته است بسیار ساده‌است. 

Prototypes

مفهوم prototypes را به همراه مثال‌های زیر و مدل ذهنی که تا بحال آموختیم شرح خواهیم داد.
به کد زیر و مدل ذهنی آن توجه کنید.

let human = {
  teeth: 32
};

let gwen = {
  age: 19
};

در مثال بالا شیئ که gwen به آن متصل است پراپرتی teeth را ندارد پس در نتیجه خروجی کد زیر undefined خواهد بود.

console.log(gwen.teeth); // undefined

فرض کنیم می‌خواهیم به جاوااسکریپت بگوییم در صورتی که یک پراپرتی در یک شیئ مثل پراپرتی teeth در شیئ gwen وجود نداشت به دنبال آن پراپرتی در یک شیئ دیگری مانند human بگردد و مقدار آن را برگرداند، این دقیقا همان مفهوم prototype است.
ما با تعریف یک پراپرتی به نام کلیدی __proto__ مشخص می‌کنیم که درصورت پیدا نشدن یک پراپرتی در یک شیئ جاوااسکریپت برای یافتن آن در کدام شیئ دیگری می‌تواند جستوجو کند. به مثال زیر توجه کنید.

let human = {
  teeth: 32
};

let gwen = {
  // We added this line:
  // "Look for other properties here"
  __proto__: human,
  age: 19
};

مدل ذهنی قطعه کد بالا

در نتیجه پس از این تغییرات خروجی کد زیر برابر 32 می‌شود.

console.log(gwen.teeth); // 32

نگاه دقیقتر به فرایندی که شرح دادیم.

مفهوم The Prototype Chain

مفهوم زنجیره prototype به این اشاره می‌کند که جاوااسکریپت عملیات جستوجو در prototype را مشابه شئ اول را همیشه تکرار می‌کند و زمانی متوقف می‌شود که یا آن پراپرتی را پیدا کند و یا مقدار __proto__ یک شیئ تعریف نشده باشد.
مثال زیر نمونه‌ای از این مفهوم زنجیره prototype است.

let mammal = {
  brainy: true,
};

let human = {
  __proto__: mammal,
  teeth: 32
};

let gwen = {
  __proto__: human,
  age: 19
};

console.log(gwen.brainy); // true

پراپرتی اصلی یک آبجکت یا یک Prototype

برای تشخیص این مورد کافیست از متد hasOwnProperty استفاده کنیم در صورتی که پراپرتی در آبجکت موجود باشد مقدار ture در غیر اینصورت مقدار false را برمی‌گرداند.

console.log(gwen.hasOwnProperty('brainy')); // false
console.log(mammal.hasOwnProperty('brainy')); // true

همانطور که قبلا اشاره کردیم مفهوم prototype صرفا به مفهوم جستوجوی جاوااسکریپت برای یافتن یک مقدار اشاره دارد و نه چیزی بیشتر پس با توجه به آنچه تا بحال آموختیم با عملیات انتصاب فقط پراپرتی جدید teeth در شیئ gwen ایجاد شده و به مقدار 31 متصل می‌شود.

The Object Prototype

به شیئ زیر توجه کنید، هیچ prototype ای برای آن تعریف نکردیم درسته؟

let obj = {};

حال قطعه کد زیر را در console مرورگر خود اجرا کنید.

let obj = {};
console.log(obj.__proto__); // Play with it!

با کمال تعجب مقدار __proto__ برابر undefined یا null نیست بلکه شامل مقادیر مختلفی مانند تابع toString و hasOwnProperty و ... است. با این اوصاف اونطور که ما تصور کردیم ما با استفاده از {} صرفا یک شیئ خالی ایجاد نکردیم بلکه جاوااسکریپت بصورت پیش فرض یک پراپرتی __proto__ تعریف کرده که به شیئ ای اشاره می‌کند که به آن The Object Prototype می‌گوییم.

برای ایجاد یک آبجکت بدون prototype پیش فرض زمان تعریف آن می‌توان مقدار __proto__ را برابر null قرار داد.

let weirdo = {
  __proto__: null
};
console.log(weirdo.hasOwnProperty); // undefined
console.log(weirdo.toString); // undefined

آلوده کردن prototype یا Polluting the Prototype

همانطور که تا الان متوجه شدیم تمام object ها بصورت پیش فرض شامل یک پراپرتی __proto__ هستن که به یک شیئ که نام آن را The Object Prototype گذاشتیم اشاره می‌کند.

به کد زیر توجه کنید.

let obj = {};
obj.__proto__.smell = 'banana';

با این کار ما تغییراتی در prototype پیش فرض ایجاد کردیم که prototype پیش فرض تمام object هایی بوده که تا بحال ساخته‌ایم و که نتایج زیر را به دنبال دارد.

console.log(sherlock.smell); // "banana"
console.log(watson.smell); // "banana"

تغییر دادن prototype مشترک بین object های مختلف را آلوده کردن prototype یا Polluting the Prototype می‌گویند.

در گذشته برای افزودن ویژگی‌های سفارشی جدید به جاوااسکریپت از این روش بسیار استفاده می‌شد ولی با گذشت سالها جامعه وب متوجه شدن که اینکار باعث ایجاد شکنندگی می‌شود و اضافه کردن قابلیت‌های جدید زبان را دشوارتر می‌کند و ترجیح دادن از آن دوری کنند.

حالا شما می‌تونید معمای مثال اول را حل کرده و در console مرورگر نتایج را تست کنید.

__proto__  یا prototype

ممکن است شما عناوین صفحه‌ی MDN را مشاهده کنید و برای شما سوال پیش بیاید که پراپرتی prototype چیست.

خبر بد: prototype تقریبا هیچ ربطی به مفهوم prototypes و __proto__ که تا بحال راجع به آن صحبت کردیم ندارد بلکه مرتبط با عملگر new در جاوااسکریپت است.

پس به یاد داشته باشید __proto__ همان نمونه‌ی اولیه یا یک prototype از یک شیئ است.
در صورتی که پرارپتی prototype یک شیئ و عملگر new دو مفهوم کاملا مجزا هستند.

§ فهرست « دنیای جاوااسکریپت »
  1. 1405.07.18هزار آیکون در یک میلی‌ثانیه: کش کردن رسم SVG به‌جای memoکامپوننت آیکونی که SVG را از داده می‌سازد در هر رندر کمی کار اضافه می‌کند. اندازه گرفتم memo کجا کمک می‌کند و کجا نه، و با نگه داشتن عناصر رسم هر آیکون، رندر مجدد هزار آیکون را از ۳.۶ به حدود ۱ میلی‌ثانیه رساندم.
  2. 1405.07.04ساختن هوکی شبیه useSyncExternalStore از صفربا useState و useEffect قدم‌به‌قدم هوکی می‌سازم که به یک store بیرونی subscribe شود، باگ هر نسخه را نشان می‌دهم و آخر کار توضیح می‌دهم چرا نسخه‌ی خودِ ری‌اکت کاری می‌کند که از کد کاربر برنمی‌آید.
  3. 1405.06.29مشکل Tearing در ری‌اکت چیست؟تیرینگ زمانی رخ می‌دهد که یک مقدار بیرون از چرخه‌ی رندر ری‌اکت حین رندر تغییر کند و کامپوننت‌های مختلف در یک فریم، دو مقدار متفاوت از همان داده را نشان بدهند.
  4. 1405.06.29چطور در دیجیکالا با useSyncExternalStore جلوی رندرهای اضافه را گرفتیموقتی داده‌ی ویجت‌های یک لیست با تأخیر و به‌صورت پلکانی می‌رسد، نگه‌داشتن آن در یک state یا Context مشترک باعث می‌شود کل لیست با هر آپدیت دوباره رندر شود؛ useSyncExternalStore به‌همراه React.memo راه می‌دهد فقط همان ویجتی که داده‌اش عوض شده رندر شود.
  5. 1399.06.03دنیای جاوااسکریپت - مقدمهآموزش ساده و روان اصول جاوااسکریپت با مدل ذهنی Dan Abramov
  6. 1399.06.01دنیای جاوااسکریپت ۵ - Prototypesمفهوم ساده‌ی Prototypes یا نمونه اولیه در جاوااسکریپت
  7. 1399.05.31دنیای جاوااسکریپت ۴ - mutate یا تغییر دادن object"مثال‌هایی از تغییر در object
  8. 1399.05.31دنیای جاوااسکریپت ۳ - انواع روش‌های مقایسه برابریانواع روش‌های مقایسه در جاوااسکریپت Object.is
  9. 1399.05.31دنیای جاوااسکریپت ۲ - متغیرها در جاوااسکریپتمتغیرها یا Variables در جاوااسکریپت و تفاوت آنها با مقادیر یا Values
  10. 1399.05.24دنیای جاوااسکریپت ۱ - انواع مقدارها یا Valuesجاوااسکریپت شامل ۹ نوع از مقادیر هست که ۷ مورد آن از نوع Primitive و یا اولیه و اصلی و ۲ مورد دیگر که از نوع Object و Function می‌باشند.