Худшее название когда-либо? :D Итак, я наконец понял, в чем дело со свойством прототипа.
Также в своем последнем посте я дал ответ на вопрос Что нам делать вместо того, чтобы использовать классы в JS? На что я ответил: довольно часто модулей будет достаточно. Здесь я дам второй ответ: когда нам нужна функциональность классов, создание объектов, связанных с другими объектами (OLOO) с помощью Object.create(), может дать нам ту же функциональность более прозрачным и явным образом, и более идиоматично. для JavaScript.
.__proto__ довольно просто, так как мы можем запросить его с помощью console.log… Для любого объекта, foo, просто введите console.log(foo.__proto__), и мы найдем объект, напечатанный в консоли. Если foo является «экземпляром класса, это будет класс. (См. мой предыдущий пост, чтобы понять, почему я использую пугающие кавычки.) В противном случае это, вероятно, будет глобальный корневой объект.
Так что насчет [[Prototype]]? Видимо, так это внутренне представлено. По сути, думайте о [[Prototype]] как о секретном частном свойстве, а .__proto__ является геттером для этого свойства. Таким образом, они оба ссылаются на ссылку прототипа.
А как насчет свойства .prototype? Как получается, что эти три формы функционально идентичны? А зачем нам .prototype во втором?
Хорошо…
«Все функции по умолчанию ссылаются на пустой объект в свойстве с именем прототип. Несмотря на запутанное название, это не прототип функции (где функция связана с прототипом), а скорее объект-прототип, на который ссылаются, когда другие объекты создаются путем вызова функции с помощью new».
Симпсон, Кайл. Вы еще не знаете JS: начните (стр. 110). GetiPub и Leanpub. Киндл издание.
Итак, это все. Во второй версии, синтаксисе класса до ES6, вопреки тому, что вы могли подумать, функция Cat на самом деле не является прототипом Cat… Это функция-конструктор для создания нового кота с новое ключевое слово. Cat.prototype — это прототип кота. Когда вы делаете «new Cat()… ну, происходит несколько вещей, вторая из которых заключается в том, что новый объект получает прототип, связанный (через .__proto__/[[Prototype]]) с любым объектом, на который указывает Cat.prototype.
В третьем примере мы пропускаем посредника. Вместо определения функции-конструктора мы явно определяем прототип лисы с литералом объекта. Это просто объект, как и все прототипы.
Обратите внимание на нижний регистр, потому что по соглашению верхний регистр используется только для вещей, которые мы должны вызывать с помощью ключевого слова «new», то есть: «new Dog()». Попытка вызвать fox с «новым» или без него вызовет ошибку, потому что это не функция.
Кайл использует аббревиатуру OLOO (объекты, связанные с другими объектами) как способ описания разновидности ООП (объектно-ориентированного программирования) в JavaScript в целом, а также для этого способа использования связи прототипа для совместного использования методов в качестве альтернативы двум предыдущим подходам. показано.
Для простоты я не присваивал «экземплярам» собственные значения; свойство «голос» эквивалентно тому, что было бы статическим свойством (на уровне класса) в традиционном ООП. Какой-то побеждает точку.
Но это само по себе важный момент: в JS нам не нужно ничего из этого для создания объекта. Мы можем просто определить объект в литерале объекта. И довольно часто этого будет вполне достаточно, и это будет самый простой и прямой и, следовательно, лучший подход.
Но что, если мы хотим создать эквивалент типичного класса в этом стиле OLOO? Ну, поскольку это JS, мы можем делать это разными способами. Заводская функция в одном варианте. Но вот как рекомендует Кайл:
Метод init выполняет работу метода конструктора в синтаксисе класса или функции конструктора в другом подходе. Его можно было бы назвать как угодно, но «init» кажется разумным стандартом.
Это включает в себя один дополнительный шаг: вызов .init() после Object.create. Но возврат «this» в методе инициализации позволяет мне связать вызов инициализации с Object.create() и сделать все это в одной строке. Я мог бы пропустить «return this», но тогда мне пришлось бы вызывать «zorro.init()» в отдельной строке.
Но если это кажется слишком большой «дополнительной работой», вы можете создать фабричную функцию, чтобы сделать ее еще проще:
const newFox = (voice) => Object.create(fox).init(voice); const zorro = newFox("Wa-pa-pa-pa-pa-pa-pow!");
С JS возможности безграничны. Это удивительно гибкий язык, который может быть как силой, так и слабостью. Но я думаю, что важно понимать, что вы на самом деле делаете, чтобы вы могли сделать осознанный выбор.
Если нам нужен «стандартный» способ реализации этого подхода «OLOO», то способ Кайла, по крайней мере, такой же хороший выбор, как и любой другой. Я согласен с ним в том, что предпочтительнее использовать ключевое слово class, потому что оно делает то же самое, но более ясно и ясно показывает, что вы на самом деле делаете: создаете объект, прототип которого связан с другим объектом.
Это больше похоже на нативный, идиоматический JS, в отличие от использования синтаксического сахара для сложного хака, чтобы имитировать шаблон, распространенный в языках, построенных на концепции «класса», в языке, конструкция которого по своей сути чужда и может быть только смоделирована.