Я пытаюсь понять за кулисами сцены Javascript и застрял в понимании создания встроенных объектов, особенно Объекта и Функции, и отношений между ними.
Когда я прочитал, что все встроенные объекты, такие как Array, String и т. Д., Являются расширением (унаследованным) от Object, я предположил, что Object является первым встроенным объектом, который создается, а остальные объекты наследуют от него. Но это не имеет смысла, когда вы узнаете, что Объекты могут быть созданы только функциями, но тогда функции также являются не чем иным, как объектами Function. Это начало звучать как дилемма курицы и курицы.
Другая чрезвычайно запутанная вещь заключается в том, что если console.log(Function.prototype)
я печатаю функцию, но когда я печатаю, console.log(Object.prototype)
она печатает объект. Почему Function.prototype
функция, когда она должна была быть объектом?
Кроме того, согласно документации Mozilla, каждый javascript function
является расширением Function
объекта, но когда вы console.log(Function.prototype.constructor)
снова - это функция. Теперь, как вы можете использовать что-то, чтобы создать это самостоятельно (Mind = Blowed).
И последнее, Function.prototype
это функция, но я могу получить доступ к constructor
функции Function.prototype.constructor
, значит ли этоFunction.prototype
функцию, которая возвращает prototype
объект
источник
Function.prototype
может быть функцией и иметь внутренние поля. Так что нет, вы не выполняете функцию прототипа при прохождении ее структуры. Наконец, помните, что есть движок, интерпретирующий Javascript, поэтому объект и функция, вероятно, создаются внутри движка, а не из Javascript и специальных ссылокFunction.prototype
иObject.prototype
могут просто интерпретироваться движком специальным образом.Ответы:
Это сложно, это легко понять неправильно, и многие книги для начинающих по Javascript ошибаются, поэтому не доверяйте всему, что читаете.
Я был одним из разработчиков движка Microsoft JS в 1990-х годах и в комитете по стандартизации, и я сделал несколько ошибок, составив этот ответ. (Хотя, поскольку я не работал над этим более 15 лет, я, возможно, могу быть прощен.) Это сложная вещь. Но как только вы понимаете наследование прототипа, все становится понятным.
Начните с отбрасывания всего, что вы знаете о наследовании на основе классов. JS использует наследование на основе прототипов.
Затем убедитесь, что у вас есть очень четкое определение того, что означает «наследование». Люди, привыкшие к ОО-языкам, таким как 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
ISFunction.prototype
- это одна из тех редких ситуаций , когдаFunction.prototype
на самом деле прототипFunction
!Function.prototype
является объектом-прототипом функции.Function.prototype
являетсяObject.prototype
Давайте предположим, что мы делаем функцию Foo.
Foo
являетсяFunction.prototype
.Foo.prototype
является прототипом объекта FooFoo.prototype
являетсяObject.prototype
.Давайте предположим, что мы говорим
new Foo()
Foo.prototype
Убедитесь, что это имеет смысл. Давайте нарисуем это. Овалы - это экземпляры объектов. Края либо
__proto__
означают «прототип», либоprototype
означают «prototype
свойство».Все, что требуется от среды выполнения, - это создать все эти объекты и соответственно назначить их различные свойства. Я уверен, что вы можете видеть, как это будет сделано.
Теперь давайте посмотрим на пример, который проверяет ваши знания.
Что это печатает?
Ну что
instanceof
значит?honda instanceof Car
означает "Car.prototype
равен любому объекту вhonda
цепочке прототипов России?"Да, это так.
honda
ПрототипCar.prototype
, так что мы закончили. Это печатает правда.Как насчет второго?
honda.constructor
не существует, поэтому мы консультируемся с прототипом, который естьCar.prototype
. КогдаCar.prototype
объект был создан, ему автоматически присваивалось свойство,constructor
равноеCar
, так что это правда.Теперь, что по этому поводу?
Что печатает эта программа?
Опять же,
lizard instanceof Reptile
означает «Reptile.prototype
равен любому объекту вlizard
цепочке прототипов России?»Да, это так.
lizard
ПрототипReptile.prototype
, так что мы закончили. Это печатает правда.Теперь, что насчет
Вы можете подумать, что это также печатает истину, так как
lizard
был создан с,new Reptile
но вы были бы неправы. Причины этого.lizard
уconstructor
отеля? Поэтому мы смотрим на прототип.lizard
естьReptile.prototype
, который естьAnimal
.Animal
уconstructor
отеля? Итак, мы смотрим на его прототип.Animal
isObject.prototype
, иObject.prototype.constructor
создается во время выполнения и равенObject
.Мы должны были сказать
Reptile.prototype.constructor = Reptile;
в какой-то момент там, но мы не помнили!Убедитесь, что все имеет смысл для вас. Нарисуйте несколько квадратов и стрелок, если это все еще сбивает с толку.
Прототип функции определяется как функция, которая при вызове возвращает
undefined
. Мы уже знаем, чтоFunction.prototype
этоFunction
прототип, как ни странно. Итак, поэтомуFunction.prototype()
законно, и когда вы делаете это, выundefined
возвращаетесь. Так что это функция.Object
Прототип не обладает этим свойством; это не вызывается. Это просто объект.Function.prototype.constructor
это простоFunction
, очевидно. ИFunction
это функция.Вы слишком обдумываете это . Все, что требуется, - это то, что среда выполнения создает кучу объектов при запуске. Объекты - это просто таблицы поиска, которые связывают строки с объектами. Когда среда запускается, все это нужно сделать , это создать несколько десятков пустых объектов, а затем начать присваивающей
prototype
,__proto__
,constructor
и так далее свойства каждого объекта , пока они не делают график , что им нужно сделать.Будет полезно, если вы возьмете ту диаграмму, которую я дал вам выше, и добавите
constructor
к ней ребра. Вы быстро увидите, что это очень простой объектный граф, и у среды выполнения не будет проблем с его созданием.Хорошим упражнением было бы сделать это самостоятельно. Здесь я тебя начну. Мы будем использовать
my__proto__
для обозначения «прототип объекта» иmyprototype
«прототип свойства».И так далее. Можете ли вы заполнить оставшуюся часть программы для создания набора объектов, имеющих ту же топологию, что и «настоящие» встроенные объекты Javascript? Если вы сделаете это, вы обнаружите, что это очень легко.
Объекты в JavaScript - это просто таблицы поиска, которые связывают строки с другими объектами . Это оно! Здесь нет магии. Вы связываете себя узлами, потому что вы воображаете ограничения, которые на самом деле не существуют, как будто каждый объект должен был быть создан конструктором.
Функции - это просто объекты, которые имеют дополнительную возможность: вызываться. Итак, пройдите вашу маленькую программу моделирования и добавьте
.mycallable
свойство к каждому объекту, которое указывает, может ли он вызываться или нет. Это так просто.источник
__proto__
.__proto__
Объект - прототип является недействительным.__proto__
Изnew X()
внеX.prototype
. Все функциональные объекты имеют прототип функции,__proto__
кроме самого прототипа функции.Object
иFunction
и прототип функции являются функциями. Все эти правила просты и определяют топологию графа исходных объектов.У вас уже есть много отличных ответов, но я просто хочу дать короткий и четкий ответ на ваш ответ о том, как все это работает, и этот ответ:
МАГИЯ !!!
На самом деле, это все.
Люди, которые реализуют механизмы исполнения ECMAScript, должны реализовывать правила ECMAScript, но не соблюдать их в своей реализации.
Спецификация ECMAScript говорит, что A наследуется от B, но B является экземпляром A? Нет проблем! Сначала создайте A с указателем прототипа
NULL
, создайте B как экземпляр A, затем исправьте указатель прототипа A, чтобы потом указать на B. Очень просто.Вы говорите, но подождите, в ECMAScript нет способа изменить указатель прототипа! Но вот в чем дело: этот код не работает на движке ECMAScript, этот код - движок ECMAScript. Он имеет доступ к внутренностям объектов, ECMAScript код работает на двигателе не имеет. Короче говоря: он может делать все, что захочет.
Кстати, если вы действительно хотите, вам нужно сделать это только один раз: после этого вы можете, например, вывести свою внутреннюю память и загружать этот дамп каждый раз, когда запускаете свой движок ECMAScript.
Обратите внимание, что все это по-прежнему применимо, даже если сам механизм ECMAScript был написан на ECMAScript (как, например, на самом деле в случае с Mozilla Narcissus). Даже в этом случае код ECMAScript, который реализует движок, все еще имеет полный доступ к движку, который он реализует , хотя он, конечно, не имеет доступа к движку, которым он является работает .
источник
Из спецификации ECMA 1
Я не понимаю, как это может быть более понятно !!!
</sarcasm>
Далее мы видим:
Таким образом, мы можем видеть, что прототип является объектом, но не обязательно объектом функции.
Кроме того, у нас есть эта интересная сиська
http://www.ecma-international.org/ecma-262/8.0/index.html#sec-object-objects
и
источник
sarcasm
противном случае ваш текст действительно непрозрачен для новичка.Следующие типы охватывают каждое значение в JavaScript:
boolean
number
undefined
(который включает в себя одно значениеundefined
)string
symbol
(абстрактные уникальные «вещи», которые сравниваются по ссылке)object
Каждый объект (то есть все) в JavaScript имеет прототип, который является своего рода объектом.
Прототип содержит функции, которые также являются своего рода объектом 1 .
У объектов также есть конструктор, который является функцией и, следовательно, своего рода объектом.
Это все рекурсивно, но реализация способна сделать это автоматически, потому что, в отличие от кода JavaScript, она может создавать объекты без вызова функций JavaScript (поскольку объекты - это просто память, которой управляет реализация).
Большинство объектных систем во многих динамически типизированных языках имеют круглую форму 2, как это. Например, в Python классы - это объекты, а класс классов - это
type
,type
следовательно, его экземпляр.Лучшая идея - просто использовать инструменты, которые предоставляет язык, и не слишком задумываться о том, как они туда попали.
1 Функции довольно специфичны, потому что они могут быть вызваны, и они являются единственными значениями, которые могут содержать непрозрачные данные (их тело и, возможно, замыкание).
2 На самом деле это скорее измученная ветвящаяся лента, согнутая назад над собой, но «круглая» достаточно близко.
источник