Я пытаюсь понять, за кулисами 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
- Прототип
Function
IS 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
is 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
могут просто интерпретироваться движком специальным образом.