За прошедшие годы в Javascript было несколько серьезных обратных косых черт, поскольку многие программисты говорят, что ему не хватает некоторых фундаментальных концепций программирования, таких как концепции объектно-ориентированного программирования.

В какой-то момент это может быть правдой, но с точки зрения реализации это не совсем так, поскольку Javascript поставляется с богатым пакетом прототипно-ориентированного программирования, который может делать почти все, что может сделать объектно-ориентированное программирование.

В этой статье вы быстро погрузитесь в программирование, ориентированное на прототипы, что сделает вас удобнее. давай начнем…

Почти все в Javascript - это объект, а также функции. При создании функций Javascript автоматически предоставляет с ними встроенный прототип объект.

Как видно выше, функция func имеет метод, называемый прототипом, который является объектом. Этот объект-прототип никак не влияет на реализацию функции.

У объекта-прототипа есть свойство-конструктор, которое указывает на саму функцию (как на картинке выше) ... Хорошо, я знаю, вы можете спросить, но каково использование этого прототипа в функции?

Скажем, например, нам говорят смоделировать приложение, которое требует свойств пользователя и некоторых методов.

Во-первых, мы можем создать объект для каждого пользователя.

По соглашению функции-конструкторы (функции, которые вызываются с ключевым словом ‘new’) должны начинаться с заглавной буквы (User), наша функция-конструктор имеет имя и возраст как параметр и метод sayName.

Эта реализация работает хорошо, но замечает, что метод sayName имеет аналогичную реализацию во всех случаях, и каждому из пользователей (user1, user2) функции User скопирован метод sayName.

Представьте, что у нас более тысячи пользователей, это означает, что у каждого из них будет копия метода sayName, который, в свою очередь, займет много места в памяти нашего приложения и снизит его производительность.

Почему бы нам не сохранить метод sayName, и пользователи могут иметь доступ к нему в любое время. Таким образом, мы предотвратили создание нескольких копий, и теперь у нас больше памяти и производительности для нашего приложения.

Здесь нам пригодится прототип: мы можем разместить наш метод sayName на прототипе конструктора функции, и наши пользователи смогут получить к нему доступ.

Исходя из вышеизложенного, мы создали почти такую ​​же функцию-конструктор, что и раньше, но на этот раз мы не добавляли к ней метод sayName напрямую, а вместо этого добавили к нему свойство prototype, используя точечную нотацию.

Обратите внимание, что метод sayName не отображается в объектах user1 и user2. Вы можете спросить, где может быть этот метод 🤔 ??? и могут ли пользователи получить к нему доступ. Чтобы ответить на этот вопрос, давайте посмотрим на приведенный ниже фрагмент…

Когда мы нажимаем на dunder proto (__proto__), мы видим, что метод sayName фактически живет внутри, также обратите внимание на конструктор, указывающий на конструктор функции User. Поэтому наши пользователи могут получить доступ к методу.

Чтобы понять, как это работает на самом деле, давайте посмотрим на диаграмму ниже.

Прототип - это объект, к которому мы можем присоединять методы и свойства, как мы это делали с методом sayName.

Когда мы создали user1 и user2, они оба имеют свойства из функции конструктора, прикрепленной к ним. Когда мы вызвали метод sayName, Javascript посмотрел на свойства пользователей и не нашел его там, но Javascript не испугается, он просматривает dunder proto (__proto__) и может получить доступ к прототипу функции конструктора пользователя. если найден, возвращает значение.

Это называется цепочкой прототипов. Этот прототип представляет собой ссылку, которая дает Javascript доступ для поиска методов и свойств в родительском прототипе. Object JS - последний в этой цепочке, вы можете назвать его прародителем, чей dunder proto указывает на null.

Исходя из фактов, мы до сих пор сделали вывод, что user1 и user2 также могут иметь доступ к методам и свойствам в прототипе Object.

user1.toString    //ƒ toString() { [native code] }
user2.valueOf     //ƒ valueOf() { [native code] }

Таким же образом, когда мы вызываем метод toString для объекта user1, Javascript сначала проверяет объект user1 и не может его найти, он не волнуется, он просматривает прототип dunder и не может найти его в прототипе функции конструктора пользователя , но он не испугается, он продолжает и просматривает ужасный прототип функции пользовательского конструктора, находит его в прототипе объекта и возвращает результат.

То, что мы сделали до сих пор, известно как инкапсуляция: мы скрыли основную реализацию пользовательской функции (метод sayName). Мы могли бы напрямую указать имя и возраст свойств в прототипе пользовательской функции, но это тоже сопряжено с некоторыми проблемами, поэтому лучше всего поместить наши свойства напрямую в функцию-конструктор и методы в прототипе, этот шаблон известен как шаблон "Конструктор / прототип".

Да здорово, теперь у вас есть хорошее понимание прототипа 😃, у вас все отлично, продолжайте.

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

Мы могли бы просто создать конструктор и связать свойства с методами в его прототипе, но некоторые из этих методов такие же, как и у функции конструктора пользователя, попытка воссоздать их - плохой способ сделать это, мы можем просто наследовать из функции конструктора пользователя, и администратор может получить к ней доступ.

Наследование

Объекты могут наследовать друг от друга в Javascript, идеальный способ сделать это - использовать метод Object.create.

Child.prototype = Object.create(Parent.prototype)

Дочерний прототип - это администратор, в то время как родительский прототип - это пользователь, как показано выше, дочерний прототип теперь имеет полный доступ к родительскому прототипу.

Одна из проблем с описанным выше методом заключается в том, что конструктор дочерней функции больше будет указывать не на дочерний элемент, а на родительский.

Child.prototype.constructor === Parent   //true

Проблему можно решить вручную, установив конструктор дочернего прототипа так, чтобы он указывал на самого себя.

Child.prototype = Object.create(Parent.prototype)
Child.prototype.constructor === Child

Теперь давайте посмотрим, как это сделать с помощью нашей модели администратора.

Обратите внимание, как мы добавили рабочую функцию, относящуюся к администратору, под строками 14 и 15, которые мы унаследовали, и вручную установили конструктор так, чтобы он указывал на себя, если мы этого не сделаем, прототип пользователя переопределит его, то есть мы не сможем чтобы получить к нему доступ.

Когда мы создавали прототип администратора, мы не создавали специально для него метод sayName, но у нас есть к нему доступ, поскольку прототип администратора унаследован от пользовательского прототипа.

Удивительно, что вы так хорошо справились 😀, вы просто научились достаточно хорошо, чтобы начать создавать хорошие вещи с помощью прототипов Javascript.

Я создал модель чат-приложения, в котором роли пользователя и администратора определены с использованием прототипно-ориентированного программирования на Javascript, проверьте это здесь, вы можете создать что-то похожее или даже сложное и лучшее :)

Резюме

Прочитав это, вы должны были узнать следующее:

  • Лучший способ создания объекта с шаблоном конструктор / прототип.
  • Как Javascript ищет методы и свойства с помощью __proto__
  • Что на самом деле означает инкапсуляция.
  • Как сделать ваш код «сухим», реализовав наследование
  • Как правильно использовать метод Object.create.

далее читается:

  1. Прототипы в Javascript
  2. Прототипное объектно-ориентированное программирование с использованием Javascript
  3. ООП в javascript