Я намагаюся зрозуміти за завісою сценами 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
може просто інтерпретуватися двигуном особливим чином.