Я пытаюсь понять, за кулисами Javascript, и застрял в понимании создания встроенных объектов, особенно Объекта и Функции, и отношений между ними.
Это сложно, это легко понять неправильно, и многие книги для начинающих по Javascript ошибаются, поэтому не доверяйте всему, что читаете.
Я был одним из разработчиков движка Microsoft JS в 1990-х годах и в комитете по стандартизации, и я сделал несколько ошибок, составив этот ответ. (Хотя, поскольку я не работал над этим более 15 лет, я, возможно, могу быть прощен.) Это сложная вещь. Но как только вы понимаете наследование прототипа, все становится понятным.
Когда я прочитал, что все встроенные объекты, такие как Array, String и т. Д., Являются расширением (унаследованным) от Object, я предположил, что Object является первым встроенным объектом, который создается, а остальные объекты наследуют от него.
Начните с отбрасывания всего, что вы знаете о наследовании на основе классов. JS использует наследование на основе прототипов.
Затем убедитесь, что у вас есть очень четкое определение того, что означает «наследование». Люди, привыкшие к ОО-языкам, таким как C # или Java или C ++, думают, что наследование означает подтип, но наследование не означает подтип. Наследование означает, что члены одной вещи также являются членами другой вещи . Это не обязательно означает, что между этими вещами есть отношения подтипов! Так много недоразумений в теории типов являются результатом того, что люди не понимают, что есть разница.
Но это не имеет смысла, когда вы узнаете, что Объекты могут быть созданы только функциями, но тогда функции также являются не чем иным, как объектами Function.
Это просто ложь. Некоторые объекты не создаются путем вызова new Fкакой-либо функции F. Некоторые объекты создаются средой выполнения JS из ничего. Есть яйца, которые не были отложены ни одной курицей . Они были просто созданы во время выполнения, когда он запускался.
Давайте скажем, каковы правила и, возможно, это поможет.
- Каждый экземпляр объекта имеет прототип объекта.
- В некоторых случаях этот прототип может быть
null.
- Если вы обращаетесь к члену в экземпляре объекта, и у объекта нет этого члена, тогда объект обращается к своему прототипу или останавливается, если прототип имеет значение null.
prototypeЧленом объекта , как правило , не прототип объекта.
- Скорее,
prototypeчлен функционального объекта F является объектом, который станет прототипом объекта, созданного с помощью new F().
- В некоторых реализациях экземпляры получают
__proto__член, который действительно дает свой прототип. (Это устарело. Не надейтесь на это.)
- Объекты функций получают совершенно новый объект по умолчанию, назначенный
prototypeпри их создании.
- Прототип функционального объекта, конечно
Function.prototype.
Давайте подведем итоги.
- Прототип
ObjectявляетсяFunction.prototype
Object.prototype является объектом-прототипом объекта.
- Прототип
Object.prototypeявляетсяnull
- Прототип
FunctionIS 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отеля? Итак, мы смотрим на его прототип.
- Прототип
Animalis 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это функция.
Теперь, как вы можете использовать что-то, чтобы создать это самостоятельно (Mind = Blowed).
Вы слишком обдумываете это . Все, что требуется, - это то, что среда выполнения создает кучу объектов при запуске. Объекты - это просто таблицы поиска, которые связывают строки с объектами. Когда среда запускается, все это нужно сделать , это создать несколько десятков пустых объектов, а затем начать присваивающей 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могут просто интерпретироваться движком специальным образом.