Я намагаюся зрозуміти за завісою сценами Javascript і свого роду застряг у розумінні створення вбудованих об'єктів, особливо Об'єкта і Функції та співвідношення між ними.
Це складно, його легко зрозуміти, і дуже багато початківців Javascript-книжок помиляються, тому не довіряйте всьому прочитаному.
Я був одним із впроваджувачів двигуна JS Microsoft у 90-х роках та в комітеті зі стандартизації, і я допустив ряд помилок, збираючи цю відповідь. (Хоча, оскільки я над цим не працював більше 15 років, можливо, мені пробачать.) Це складні речі. Але як тільки ви зрозумієте успадкування прототипу, це все має сенс.
Коли я прочитав, що всі вбудовані об'єкти, такі як Array, String тощо, є розширенням (успадкованим) від Object, я припустив, що Object є першим вбудованим об'єктом, який створюється, а решта об'єктів успадковується від нього.
Почніть з викидання всього, що ви знаєте про спадкування на основі класу. JS використовує спадкування на основі прототипу.
Далі переконайтеся, що у вас в голові є дуже чітке визначення того, що означає «спадщина». Люди, які звикли до мов OO, таких як C # або Java або C ++, вважають, що успадкування означає субтипізацію, але успадкування не означає підтипізацію. Спадщина означає, що члени однієї речі також є членами іншої речі . Це не обов'язково означає, що між цими речами існує взаємозв'язок! Так багато непорозумінь в теорії типів є результатом того, що люди не розуміють, що є різниця.
Але це не має сенсу, коли ви дізнаєтесь, що Об'єкти можна створювати лише за допомогою функцій, але тоді функції - це не що інше, як об'єкти Функції.
Це просто помилково. Деякі об'єкти не створюються за допомогою виклику new Fпевної функції F. Деякі об'єкти створюються в процесі виконання JS взагалі з нічого. Є яйця, які не відклала жодна курка . Вони були просто створені під час виконання, коли він запускався.
Скажімо, які правила є, і, можливо, це допоможе.
- Кожен екземпляр об'єкта має об'єкт-прототип.
- У деяких випадках прототипом може бути
null.
- Якщо ви отримуєте доступ до члена в екземплярі об'єкта, а об’єкт не має цього члена, то об'єкт переходить до свого прототипу або зупиняється, якщо прототип є нульовим.
prototypeЧленом об'єкта , як правило , НЕ прототип об'єкта.
- Скоріше,
prototypeчлен функціонального об'єкта F - це об'єкт, який стане прототипом об'єкта, створеного new F().
- У деяких реалізаціях екземпляри отримують
__proto__член, який дійсно надає свій прототип. (Це тепер застаріло. Не покладайтеся на це.)
- Об'єкти функцій отримують абсолютно новий об'єкт за замовчуванням, призначений під
prototypeчас їх створення.
- Зрозуміло, прототип функціонального об'єкта
Function.prototype.
Давайте підведемо підсумки.
- Прототипом
ObjectєFunction.prototype
Object.prototype є об'єктом прототипу об'єкта.
- Прототипом
Object.prototypeєnull
- Прототип
Functionє Function.prototype- це одна з рідкісних ситуацій, коли Function.prototypeнасправді є прототипом Function!
Function.prototype є об'єктом прототипу функції.
- Прототипом
Function.prototypeєObject.prototype
Припустимо, ми робимо функцію Foo.
- Прототипом
Fooє Function.prototype.
Foo.prototype є об'єктом прототипу Foo.
- Прототипом
Foo.prototypeє Object.prototype.
Припустимо, ми говоримо new Foo()
- Прообразом нового об’єкта є
Foo.prototype
Переконайтесь, що це має сенс. Давайте намалюємо це. Овали - це об'єктні екземпляри. Краї є або __proto__означають "прототип", або prototypeозначають " prototypeвластивість".

Все, що потрібно виконати, - це створити всі ці об'єкти і відповідно призначити їх різні властивості. Я впевнений, що ви можете бачити, як це було б зробити.
Тепер давайте розглянемо приклад, який перевіряє ваші знання.
function Car(){ }
var honda = new Car();
print(honda instanceof Car);
print(honda.constructor == Car);
Що це друкує?
Ну, що instanceofозначає? honda instanceof Carозначає " Car.prototypeдорівнює будь-якому об'єкту в hondaланцюзі прототипу '?"
Так. hondaПрототип є Car.prototype, тому ми зробили. Це друкує правду.
А як щодо другого?
honda.constructorне існує, тому ми консультуємося з прототипом, який є Car.prototype. Коли Car.prototypeоб’єкт був створений, йому автоматично було надано властивість, constructorрівне Car, тож це правда.
А що з цим?
var Animal = new Object();
function Reptile(){ }
Reptile.prototype = Animal;
var lizard = new Reptile();
print(lizard instanceof Reptile);
print(lizard.constructor == Reptile);
Що друкує ця програма?
Знову ж таки, lizard instanceof Reptileозначає " Reptile.prototypeдорівнює будь-якому об'єкту в lizardланцюзі прототипу '?"
Так. lizardПрототип є Reptile.prototype, тому ми зробили. Це друкує правду.
А тепер про що
print(lizard.constructor == Reptile);
Ви можете подумати, що це також друкує правду, оскільки lizardбуло побудовано з, new Reptileале ви будете помилятися. Обґрунтуйте це.
- Чи
lizardмає constructorмайно? Ні. Тому ми розглянемо прототип.
- Прототип
lizardє Reptile.prototype, який є Animal.
- Чи
Animalмає constructorмайно? Ні. Отже, ми подивимось, що це прототип.
- Прототип
Animalє Object.prototype, і Object.prototype.constructorстворюється під час виконання та дорівнює Object.
- Так що це друкує помилково.
Ми повинні були сказати Reptile.prototype.constructor = Reptile;в якийсь момент там, але ми цього не пам’ятали!
Переконайтесь, що для вас все має сенс. Намалюйте кілька полів і стрілок, якщо це все ще заплутано.
Інша надзвичайно заплутана річ - це, якщо я console.log(Function.prototype)друкує функцію, але коли я друкую, console.log(Object.prototype)вона друкує об'єкт. Чому Function.prototypeфункція, коли вона мала бути предметом?
Прототип функції визначається як функція, яка при виклику повертається undefined. Ми вже знаємо, що Function.prototypeце Functionпрототип, як не дивно. Отже, Function.prototype()це законно, і коли ти це робиш, ти undefinedповертаєшся. Отже, це функція.
ObjectПрототип не володіє цією властивістю; це не можна телефонувати. Це просто предмет.
коли ти console.log(Function.prototype.constructor)це знову функція.
Function.prototype.constructorпросто Function, очевидно. І Functionце функція.
Тепер, як ви можете використовувати щось для створення себе (Розум = здутий).
Ви надмірно думаєте про це . Все, що потрібно, це те, що час запуску створює купу об’єктів при його запуску. Об'єкти - це просто таблиці пошуку, які пов'язують рядки з об'єктами. Коли середовище запускається, все це потрібно зробити , це створити кілька десятків порожніх об'єктів, а потім почати присвоює prototype, __proto__, constructorі так далі властивості кожного об'єкта , поки вони не роблять графік , що їм потрібно зробити.
Буде корисно, якщо ви візьмете ту схему, яку я вам подав вище, і додати constructorдо неї краї. Ви швидко переконаєтесь, що це дуже простий графік об'єкта і що його час не матиме жодних проблем створити його.
Гарною вправою було б зробити це самостійно. Ось, я розпочну вас. Ми будемо використовувати my__proto__для позначення "об'єкт прототипу" та myprototype"властивість прототипу".
var myobjectprototype = new Object();
var myfunctionprototype = new Object();
myfunctionprototype.my__proto__ = myobjectprototype;
var myobject = new Object();
myobject.myprototype = myobjectprototype;
І так далі. Чи можете ви заповнити решту програми для побудови набору об'єктів, що має ту саму топологію, що і "справжні" вбудовані об'єкти Javascript? Якщо ви зробите це, то ви побачите, що це надзвичайно просто.
Об'єкти в JavaScript - це просто таблиці пошуку, які пов'язують рядки з іншими об'єктами . Це воно! Тут немає ніякої магії. Ви зав'язуєте себе у вузлах, тому що уявляєте обмеження, яких насправді не існує, як, що кожен об'єкт повинен був створювати конструктор.
Функції - це просто об'єкти, які мають додаткову можливість: викликатись. Тому перегляньте свою маленьку програму моделювання та додайте .mycallableдо кожного об'єкта властивість, яка вказує, чи можна її називати чи ні. Це так просто.
Function.prototypeможе бути функцією і мати внутрішні поля. Тому ні, ви не виконуєте функцію прототипу, переглядаючи її структуру. Нарешті, пам’ятайте, що існує двигун, що інтерпретує Javascript, тому «Об’єкти та функції», ймовірно, створюються всередині двигуна, а не з Javascript та спеціальних довідок, як,Function.prototypeіObject.prototypeможе просто інтерпретуватися двигуном особливим чином.