نمونه اولیه JavaScript و __proto__ – آیا می دانید چگونه کار می کند؟

tl ؛ دکتر:
در جاوا اسکریپت ، همه وراثت است نمونه اولیه – به این معنی که هر شیء از طریق یک زنجیره به نام از شیء دیگر به ارث می برد
__proto__
بشر در پشت صحنه ، حتی کلاس ها و آرایه ها به این مکانیسم متکی هستند. این پست چه چیزی را تجزیه می کند__proto__
وتprototype
واقعاً ، چگونه آنها متفاوت هستند ، چگونه زنجیره های وراثت تشکیل می شوند و چگونه سازندگان رفتار را پایین می آورند. اگر تا به حال در مورد نحوه اشتراک اشیاء یا چرا می توانید از آن استفاده کنیدtoString()
در مورد همه چیز – این یکی برای شما است.
مقدمه
خوش آمدید قسمت 2 سریال “آیا می دانید چگونه کار می کند؟”
اگر در حال دنبال کردن هستید ، به یاد خواهید آورد که همه چیز شروع شد وقتی که من یک پست نوشتم توابع فلش (که معلوم شد قسمت 0). پاسخ به آن پست – پر از سوالات روشنگری – باعث شد من متوجه شوم که بسیاری از شیاطین هنوز در مورد آن مطمئن نیستند مکانیک اصلی از جاوا اسکریپت.
بنابراین ما در اینجا هستیم ، در ادامه سفر.
موضوع امروز یکی از سوء تفاهم ترین جنبه های جاوا اسکریپت است: تفاوت بین __proto__
وت prototype
بشر
اگر تا به حال فکر کرده اید:
- چرا اشیاء شما می توانند استفاده کنند
.toString()
حتی اگر هرگز آن را تعریف نکرده اید ، - چه اتفاقی می افتد هنگام استفاده
class
، یا - چه
Array.prototype
در واقع یعنی …
سپس این پست برای شما مناسب است. بیایید به واقعی مکانیک وراثت جاوا اسکریپت.
همانطور که در موضوع وراثت دیدیم ، جاوا اسکریپت در مقایسه با سایر زبانهای برنامه نویسی ، وراثت را متفاوت می کند. ما در مورد صحبت می کنیم ارث اولیهبشر اما این در عمل به چه معنی است؟ بیایید نگاهی دقیق تر بیندازیم.
const person = {};
console.log(person); // Object { __proto__: Object }
در اینجا ، ما خالی اعلام می کنیم person
شیء. اما اگر محتوای آن را بازرسی کنیم ، می بینیم که واقعاً خالی نیست – حاوی یک __proto__
ویژگی (گاهی به عنوان نشان داده شده است
در مرورگر) ، حتی اگر ما صریحاً آن را اعلام نکردیم.
خوب ، برای درک این موضوع ، ما باید کمی عمیق تر پیش برویم. بیایید این کار را انجام دهیم:
person.name = "Alan Turing";
person.age = 41;
console.log(person.name); // Alan Turing
console.log(person.age); // 41
console.log(person.nationality); // undefined
console.log(person.speak); // undefined
نتایج در اینجا همانطور که انتظار می رود – هر آنچه که ما صریحاً بازده را تعریف نکردیم undefined
بشر اما استثنائاتی وجود دارد:
person.toString; // function toString();
person.valueOf; // function valueOf();
حتی اگر ما این کارکردها را تعریف نکردیم ، آنها برنمی گردند undefined
بشر در واقع ، اگر آنها را اجرا کنیم ، آنها نتیجه می گیرند. اما چرا این است؟ آنها از کجا آمده اند؟
__proto__
در واقع ، هر وقت متغیر یا ثابت را با یک شیء به عنوان مقدار آن اعلام می کنیم – مهم نیست که چگونه ایجاد شده است (const obj = {}
با const obj = new Object()
، یا Object.create({})
) – همیشه یک __proto__
خاصیت این همه چیزهایی را که شیء به عنوان نمونه ای از آن به ارث می برد نگه می دارد اعتراض (سرمایه O).
این منحصر به اشیاء نیست. بسته به نوع متغیر ایجاد شده ، آن را از یک متفاوت به ارث می برد سازندهبشر سازندگان اساساً پایه های جاوا اسکریپت هستند – آنچه از هر نوع ساخته شده است.
به عنوان مثال:
const cars = [];
console.log(cars); // Array [length: 0, __proto__: Array[]]
هنگام ایجاد یک آرایه ، می توانیم از قبل ببینیم که دارای برخی از ویژگی های خاص است: a length
ویژگی ، و آن __proto__
است ، Array[]
بشر این بدان معنی است که با استفاده از Array
سازنده ، که تعریف می کند که آرایه ها دارای یک هستند length
و برخی از روش های داخلی
cars.push("Aston Martin");
console.log(cars.length); // 1
console.log(cars.pop()); // Aston Martin
console.log(cars.length); // 0
به همین دلیل می توانیم از روش هایی مانند استفاده کنیم Array.push()
وت Array.pop()
بشر و به همین دلیل است که مستندات اغلب این روشها را با پیشوند نشان می دهد Array.
– آنها روشهای Array
کلاس که همه آرایه ها از آن به ارث می برند.
اما __proto__
منحصر به انواع پیچیده مانند اشیاء ، آرایه ها یا مجموعه ها نیست. هر نوع در جاوا اسکریپت توسط یک نمونه اولیه تعریف می شود.
const fruit = "Banana";
console.log(fruit.__proto__); // String { "" }
const number = 10;
console.log(number.__proto__); // Number { 0 }
const boolean = true;
console.log(boolean.__proto__); // Boolean { false }
همه انواع ابتدایی سازه های خاص خود را دارند و ویژگی ها و روش های خاص خود را به ارث می برند. همچنین ، در داخل خروجی سازنده ، اغلب مقداری در بریس های فرفری مانند مشاهده می کنید. ""
با 0
، یا false
– این مقدار پیش فرض اولیه آن نوع را نشان می دهد.
بنابراین ، اگر ما با استفاده از آن سازندگان بدون عبور از هر مقدار متغیر ایجاد کنیم ، با آن پیش فرض ابتدایی آغاز می شود:
const stringTest = new String();
const numberTest = new Number();
const boolTest = new Boolean();
console.log(stringTest.valueOf()); // ""
console.log(numberTest.valueOf()); // 0
console.log(boolTest.valueOf()); // false
حتی جالب تر ، هر یک از این سازندگان (رشته ، شماره ، بولی و غیره) نیز خاص خود را دارند __proto__
بشر
const someVar = "";
console.log(someVar.__proto__.__proto__); // Object { }
وقتی آخرین بار را باز می کنیم __proto__
، می بینیم که این همان چیزی است که هنگام ایجاد یک شیء خالی ساده به دست می آوریم. بنابراین می توانیم نتیجه بگیریم: String
وراثت از Object
، و یک متغیر ایجاد شده از String
وراثت از String
بشر
این تأیید می کند که حتی انواع مانند رشته در نهایت از شیء به ارث می برند و تشکیل می دهند __proto__
زنجیره ای (به نام زنجیره نمونه اولیه) که همیشه به ریشه جاوا اسکریپت باز می گردد: Object
بشر بیایید این مفهوم را کشف کنیم:
const being = {
type: "Living Being"
};
const species = Object.create(being);
species.name = "Human";
const person = Object.create(species);
person.age = 25;
console.log(person);
/*
Object {
age: 25,
__proto__: Object {
name: "Human",
__proto__: Object {
type: "Living Being",
__proto__: Object {...}
}
}
}
*/
هنگام بازرسی person
شی ، ما می بینیم که همه چیز را از آن به ارث می برد species
(که به عنوان یک طرح استفاده می شد) و از being
(برای ایجاد استفاده می شود species
) – و در نهایت از Object
بشر
بیایید کمی بیشتر برویم:
const person2 = Object.create(species);
person2.name = "Dwight";
console.log(person.name); // Human
console.log(person2.name); // Dwight
هنگام چاپ person.name
، ما از “انسان” می گیریم species.name
بشر اما person2.name
“دویت” است ، حتی اگر از آن ایجاد شده باشد species
بشر
به این دلیل است که person2
خودش را دریافت کرد name
ویژگی وقتی تماس می گیریم person.name
، این شیء خود را جستجو می کند ، چیزی پیدا نمی کند و به پایین می رود __proto__
زنجیره ای برای به دست آوردن ارزش. اما person2
در حال حاضر یک name
، بنابراین در آنجا متوقف می شود.
console.log(person2);
/*
Object {
name: "Dwight",
__proto__: Object {
name: "Human",
__proto__: Object {...}
}
}
*/
اگر می خواستیم name
از species
با استفاده از person2
به عنوان یک مرجع ، ما باید نام آن را تغییر دهیم __proto__
بشر این تغییر خواهد کرد species.name
و تمام اشیاء ارثی از آن را تحت تأثیر قرار می دهد.
person2.__proto__.name = "Elf";
console.log(person.name); // Elf
console.log(species.name); // Elf
این همچنین با کلاس ها کار می کند
class Vehicle {
move() {
return "Moving";
}
}
class Car extends Vehicle {
accelerate() {
return "Vroom";
}
}
class RaceCar extends Car {
compete() {
return "Jump Start!";
}
}
const mclaren = new RaceCar();
console.log(mclaren); // Object { }
mclaren.move(); // "Moving"
mclaren.accelerate(); // "Vroom"
mclaren.compete(); // "Jump Start!"
mclaren.winRace(); // undefined
اگر بازرسی کنیم mclaren
، در سطح بالا می بینیم که از آن ایجاد شده است RaceCar
سازنده آن را __proto__
است ، Car
، و سطح بعدی Vehicle
، و سرانجام Object
بشر چیزی شبیه به این:
همچنین ، mclaren
می تواند همه کارها را انجام دهد RaceCar
با Car
وت Vehicle
می تواند انجام دهد اما اگر سعی کنیم به چیزی دسترسی پیدا کنیم که در هیچ کجای آن وجود ندارد زنجیره ارث، ما می گیریم undefined
بشر
prototype
بیایید تنظیم کنیم __proto__
گذشته برای یک ثانیه. چیست prototype
و چگونه متفاوت است؟
دیدن چیزی شبیه به Array.prototype.concat()
در اسناد اما دقیقاً چیست prototype
؟
خوب ، prototype
ویژگی فقط در توابع سازنده یا کلاس ها وجود دارد (ما در یک پست آینده در مورد سازنده ، کلاس ها و برخی از موضوعات مشابه دیگر بحث خواهیم کرد).
این را بررسی کنید:
function Person(name) {
this.name = name;
}
const person = new Person("Michael Jackson");
console.log(person); // Object { name: "Michael Jackson" }
person.__proto__ === Person.prototype; // true
آنچه در اینجا می بینیم این است که Person(name)
تابع نحوه ساخت یک شی را مشخص می کند. در این حالت ، ما عبور می کنیم name
پارامتر برای تولید a Person
شیء.
همچنین ، ما می توانیم مشاهده کنیم که یک کلاس prototype
به عنوان به شیء منتقل می شود __proto__
بشر بنابراین ، اگر چیزی را در نمونه اولیه کلاس اضافه یا اصلاح کنیم ، این تغییر در تمام اشیاء آن کلاس منعکس می شود.
برعکس نیز درست است – اصلاح __proto__
از یک نمونه می تواند در نمونه اولیه کلاس و در همه موارد دیگر تأمل کند.
در اصل ، __proto__
وت prototype
به همان شیء مراجعه کنید – فقط به روش های مختلف دسترسی پیدا کنید.
اما به طور خلاصه ، این تفاوت ها هستند:
__proto__
→ روی وجود دارد هر نمونه شی
→ به شیئی که از آن به ارث می برد اشاره می کند
→ استفاده شده در زمان اجرا برای رفع خصوصیات و روشهای زنجیره وراثت
→ شما به ندرت این را به صورت دستی تنظیم می کنید (اگرچه امکان پذیر است)
prototype
→ فقط در توابع و کلاسهای سازنده
that تعریف موارد جدید ایجاد شده با new
ارث می برد
→ شما از آن استفاده می کنید روشهای مشترک را اضافه یا نادیده بگیرید
→ می شود __proto__
از مواردی که توسط آن سازنده ایجاد شده است
افکار نهایی
درک __proto__
وت prototype
گامی بزرگ برای تسلط بر نحوه کار JavaScript در زیر کاپوت است. این فقط تئوری نیست-این ستون فقرات نحوه ارتباط اشیاء ، رفتار وراثت و کارآمد در حافظه است.
این که آیا شما با اشیاء ساده کار می کنید ، از کلاس ها استفاده می کنید ، یا چیزی را با چارچوب هایی مانند React یا Vue ایجاد می کنید ، این مفهوم همیشه وجود دارد – سکوت کد خود را.
نکته من؟
با این مفاهیم آزمایش کنید ، اشیاء خود را در کنسول مرورگر بازرسی کنید و با استفاده از زنجیرهای میراث خود را بسازید Object.create()
بشر شما تعجب خواهید کرد که JavaScript چقدر واضح تر می شود پس از کلیک بر روی این لایه “نامرئی”.
actions سؤال ، بینش یا موارد استفاده جالب برای نمونه های اولیه دارید؟
نظر زیر را رها کنید – دوست دارم از شما بشنوم!
👉 و اگر از این سریال لذت می برید ، مرا دنبال کنید Matheusjulidori برای گرفتن قسمت های بعدی!
بعدی: توابع سازنده