За прошедшие годы в 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.
далее читается: