Skip to content

inheritance model

garevna edited this page Apr 5, 2019 · 26 revisions

🎓 Модель наследования JS

⤵️

Модель наследования JS основана на понятии прототипа

Прототип - это объект

Предшественником JS в плане прототипной модели наследования является язык

Стремление сделать синтаксис Javascript похожим на Java привело к появлению в языке таких "рудиментов", как ключевое слово new, лишенное практического смысла в языках с прототипной моделью наследования и создающее иллюзию наличия классов

Класс - это абстракция, объект - это воплощение

‼️ Уважаемые дамы и господа
Курение вредит вашему здоровью,
а курение конкретно в этом подъезде может вообще резко подорвать его

☕ Класс и объект

Чашка - это абстракция, но чашка, из которой ты в данный момент пьешь чай - это объект, т.е. конкретное воплощение абстракции "чашка"

Для создания конкретного экземпляра класса "чашка" в классической модели используется ключевое слово new


☕ Наследование в классической модели

Абстракция "чашка" вложена в другую абстракцию - "емкость", поскольку емкостью также является кастрюля, и цистерна, и колба

Таким образом, класс "чашка" наследует от класса "емкость"


Так вот:

в JS наследование происходит от объекта, а не класса,

т.е. от конкретного воплощения, а не от абстракции

Другими словами, JS - очень конкретный язык 😉


Для создания объекта в JS нам достаточно сделать следующее:

☕ 1

var sample = {
    name: "master"
}

При этом у нашего объекта "магическим образом" появляется свойство __proto__:

▼ { name: "master" }
    name: "master"
  ► __proto__: Object

Это свойство является ссылкой на объект prototype, который реально существует

внутри встроенного нативного объекта Object

На мой взгляд, это вполне соотносится с реальностью:

можно ли получить наследство от класса "дедушка" ?

Только конкретный дедушка может оставить вам наследство

Таким образом, sample наследовал от Object

Сам "дедушка" Object не является наследством

Логично, что наследство "дедушки" где-то хранится

Оно хранится в свойстве prototype объекта Object

Чтобы посмотреть, что же он унаследовал, давайте развернем свойство __proto__ в консоли:

▼ { name: "master" }
    name: "master"
  ► __proto__:
      ► constructor: ƒ Object()
      ► hasOwnProperty: ƒ hasOwnProperty()
      ► isPrototypeOf: ƒ isPrototypeOf()
      ► propertyIsEnumerable: ƒ propertyIsEnumerable()
      ► toLocaleString: ƒ toLocaleString()
      ► toString: ƒ toString()
      ► valueOf: ƒ valueOf()
      ► __defineGetter__: ƒ __defineGetter__()
      ► __defineSetter__: ƒ __defineSetter__()
      ► __lookupGetter__: ƒ __lookupGetter__()
      ► __lookupSetter__: ƒ __lookupSetter__()
      ► get __proto__: ƒ __proto__()
      ► set __proto__: ƒ __proto__()

Например, "наследство" включает метод hasOwnProperty

Давайте попробуем его использовать:

sample.hasOwnProperty( "name" )   // true

О-па, мы получили true, т.е. метод работает!

Значит, наследство благополучно получено 😸

И никаких классов!

⁉️ зачем нам вообще нужно ключевое слово new


☕ 2

Что мы знаем о функциях JS ?

  1. в момент вызова у них появляется ссылка на контекст вызова - this
  2. у них есть свойство prototype

Как же сделать функцию конструктором?

Очевидно, нужно создать объект, на который будет ссылаться this

Отсюда логически следует, что любая функция JS может быть конструктором и без ключевого слова new

function SampleClass () {
    this.name = "master"
}

var sample = new SampleClass ()

в результате у экземпляра sample появляется цепочка прототипов:

▼ SampleClass { name: "master" }
    name: "master"
  ▼ __proto__:
      constructor: ƒ SampleClass()
    ► __proto__: Object

первое свойство __proto__ является ссылкой на объект, который содержит:

  • свойство constructor

    ссылка на функцию SampleClass

  • еще одно свойство __proto__

    ссылка на Object

Итак, наша цепочка прототипов состоит уже из двух звеньев

и опять никаких классов...

Так зачем нам ключевое слово new ?

Только для имитации классовой модели наследования, которой нет...

А что у нас в действительности есть ?


🎓 Что такое конструктор

Любая функция может быть вызвана с ключевым словом new

Вопрос в том, что происходт


🎓 Конструктор Object

⤴️ ⤵️

Прототипом всех нативных объектов является Object

У конструктора Object есть свойство prototype

Это "генокод" всех создаваемых объектов JS

Он передается им при "рождении"

Все свойства и методы, перечисленные в свойстве prototype конструктора Object, будут доступны в каждом экземпляре создаваемого нативного объекта JS


☕ 3

var master = { name: "master" }
var child = { name: "child" }
var obj = {}
obj.__proto__ = child
obj.__proto__.__proto__ = master

Экземпляр obj в консоли:

▼ {}
    ▼ __proto__: 
        name: "child"
      ▼ __proto__: 
          name: "master"
        ► __proto__: Object

Добавим метод setName объекту __proto__

obj.__proto__.setName = function ( val ) {
    this.name = val
}

и посмотрим на объект child ( который мы не трогали ) в консоли:

▼ { name: "child", setName: ƒ }
    name: "child"
  ▸ setName: ƒ ( val )
  ▸ __proto__: Object

Это свидетельствует о том, что свойство __proto__ экземпляра является ссылкой на объект прототипа

В обоих примерах легко отслеживается цепочка прототипов

Последним "звеном" в этой цепочке будет Object - "Адам" всех нативных объектов JS

Заканчивается цепочка прототипов null

Если возникнет необходимость создать объект без прототипа, можно проделать следующее:

var obj = Object.create( null )

или так:

var obj = {}
obj.__proto__ = null

Thus, свойство __proto__ есть у любого объекта, и это свойство - ссылка на объект

❓ Где будет находиться свойство crocodile после выполнения кода:

var obj = {}
obj.__proto__.crocodile = "crocodile"
var obj = {}

obj.valueOf ()
obj.toString ()

🎓 Статические свойства Object

⤴️ ⤵️

( собственные свойства объекта Object )

А вот свойства и методы объекта Object, которые не находятся в свойстве prototype, не наследуются экземплярами, и могут быть вызваны только как свойства и методы объекта Object:

Поскольку они не передаются экземплярам, их называют статическими

☕ 3

var obj = {
     name: "test",
     type: "sample"
}
obj.__proto__.crocodile = "crocodile"

Object.getOwnPropertyNames ( obj )
Object.keys ( obj )
...

Метод Object.getOwnPropertyNames возвращает массив всех собственных свойств объекта, переданного в качестве аргумента метода

(2) ["name", "type"]

Метод Object.keys возвращает массив всех собственных перечислимых свойств объекта, переданного в качестве аргумента метода

(2) ["name", "type"]

Выведите в консоль

Object.keys ( obj )
Object.getOwnPropertyNames ( obj )

Ни первый, ни второй метод не выведут свойство crocodile, потому что это свойство не собственное ( оно находится в прототипе, т.е. является унаследованным )

А вот если итерировать obj с помощью цикла for ... in, мы получим свойство crocodile

Это означает, что свойство, добавленное нами в прототип объекта, не является собственным, но является перечислимым

Object.__proto__

⤴️ ⤵️

Выведем в консоль свойство __proto__ конструктора Object

▼ __proto__: ƒ ()
    ► apply: ƒ ()
      arguments: (...)
    ► bind: ƒ ()
    ► call: ƒ ()
      caller: (...)
    ► constructor: ƒ ()
      length: 0
      name: ""
    ► toString: ƒ ()
    ► Symbol(Symbol.hasInstance): ƒ ()
    ► get arguments: ƒ ()
    ► set arguments: ƒ ()
    ► get caller: ƒ ()
    ► set caller: ƒ ()
    ► __proto__: Object

а теперь выведем в консоль свойство prototype конструктора Function

▼ prototype: ƒ ()
    ► apply: ƒ ()
      arguments: (...)
    ► bind: ƒ ()
    ► call: ƒ ()
      caller: (...)
    ► constructor: ƒ ()
      length: 0
      name: ""
    ► toString: ƒ ()
    ► Symbol(Symbol.hasInstance): ƒ ()
    ► get arguments: ƒ ()
    ► set arguments: ƒ ()
    ► get caller: ƒ ()
    ► set caller: ƒ ()
    ► __proto__: Object

Очевидно, что Object наследует от Function, что логично, поскольку Object - это конструктор, т.е. функция

console.dir ( Object.__proto__.constructor.name )
// Function

При этом объект Function.prototype наследует от Object

( свойство Function.prototype.__proto__ является ссылкой на Object.prototype )

ссылка на
Object.__proto__ Function.prototype
Function.prototype.__proto__ Object.prototype

⤴️

💼 Упражнения


🔗 MDN 🔗 MDN

© Irina H.Fylyppova 2018
Использование данных материалов или любой их части коммерческими школами ( курсами ) является нарушением авторских прав


Новая версия


1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19

Занятие 1

⤵️

Занятие 2

⤴️ ⤵️

Занятие 3

⤴️ ⤵️

Занятие 4

⤴️ ⤵️

Занятие 5

⤴️ ⤵️

Занятие 6

⤴️ ⤵️

Занятие 7

⤴️ ⤵️

Занятие 8

⤴️ ⤵️

Занятие 9

⤴️ ⤵️

Занятие 10

⤴️ ⤵️

Занятие 11

⤴️ ⤵️

Занятие 12

⤴️ ⤵️

Занятие 13

⤴️ ⤵️

Занятие 14

⤴️ ⤵️

Занятие 15

⤴️ ⤵️

Занятие 16

⤴️ ⤵️

Занятие 17

⤴️ ⤵️

Занятие 18

⤴️ ⤵️

Занятие 19

⤴️ ⤵️

⤴️

ico20 Дополнительно
dir-20 Справочная инфо

Clone this wiki locally