Руководство для начинающих по работе с прототипами в javascript

Проверяем является ли объект экземпляром класса

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

object instanceof Class

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

function Animal (name, energy) {
  this.name = name
  this.energy = energy
}

function User () {}

const leo = new Animal('Leo', 7)

leo instanceof Animal // true
leo instanceof User // false

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

Соответственно будет возвращать , поскольку .

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

Как установить объект в качестве прототипа, мы разобрались. Но что если у нас нет конкретного объекта, а только конструктор для него.

Мы можем легко наследовать один класс (конструктор) от другого.

Создадим для начала функцию-конструктор :

Этот конструктор может создавать объекты с собственными свойствами , и , а также с доступным из прототипа методом .

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

Для этого нам нужно сделать две вещи:

1) В самом конструкторе нужно заполнить новый объект () теми же свойствами, которыми свои объекты заполняет конструктор .

2) Установить для новых объектов класса прототип с тем же набором методов, который есть в .

Теперь создаем нового кролика:

Доступность использования возможностей классов

Новые возможности использования классов, представленные в этом посте, отражены в стандарте ES2015 и предложениям, закладываемым в него на 3 этапе.

В конце 2019 года все, рассмотренные нами в посте, функциональные возможности классов представлены:

  • Публичные и приватные поля экземпляра класса являются частью Class fields proposal;
  • Приватные методы экземпляра, а также средства доступа (геттеры и сеттеры) являются частью Class private methods proposal;
  • Публичные и приватные статические поля, а также приватные статические методы являются частью Class static features proposal;
  • Все остальное является частью стандарта ES2015.

Objects in JavaScript

Objects are a major part of JavaScript, as almost everything in it is an object. For example, functions, arrays, regular expressions, dates, and even data types like boolean and strings, if declared with the keyword new, can be considered a javascript object.

What is an object?

In real-life, objects are found everywhere, so these real-life scenarios can also be mapped into object-oriented code.

Let’s take a look at an example of how Objects are used. Assume you have three shapes which you need to find the area of: square, rectangle and circle. If you were to write code that would calculate the area of each, what would you do?

In an OOP style, you’d convert the code by creating objects for each shape: square, rectangle, and circle. Here, each object has its own set of properties which include:

Наследование прототипов

Когда вы пытаетесь получить доступ к свойству или методу объекта, JavaScript сначала выполняет поиск по самому объекту, и если искомое не найдено, он будет искать объект `Prototype`. Если после поиска по объекту и его `Prototype` совпадения не найдено, JavaScript проверит прототип связанного объекта и продолжит поиск до тех пор, пока не достигнет конца цепочки прототипов.

В конце цепочки прототипов находится Object.prototype. Все объекты наследуют свойства и методы Object. Любая попытка поиска за пределами цепочки приводит к null.

В нашем примере x – пустой объект, который наследуется от Object. x может использовать любое свойство или метод, которые имеет Object, например toString().

Эта цепочка прототипов состоит из всего одной ссылки (x -> Object). Это понятно потому, что если вы попытаетесь связать два свойства `Prototype`, получится null.

Давайте рассмотрим другой тип объекта. Если у вас есть опыт работы с массивами JavaScript, вы знаете, что у них много встроенных методов (таких как pop() и push()). У вас есть доступ к этим методам при создании нового массива потому, что любой массив, который вы создаете, имеет доступ к свойствам и методам Array.prototype.

Создайте новый массив:

Помните, что создать его можно также с помощью конструктора массива: let y = new Array().

Если посмотреть на `Prototype` нового массива y, вы увидите, что он имеет больше свойств и методов, чем объект x. Он унаследовал все это от Array.prototype.

Вы увидите свойство constructor в прототипе, для которого задано значение Array(). Свойство constructor возвращает функцию-конструктор объекта, которая является механизмом для построения объектов из функций.

Теперь можно объединить два прототипа, так как в этом случае цепочка прототипов будет длиннее. Он выглядит так: y-> Array -> Object.

Эта цепочка теперь относится к Object.prototype. Можно проверить внутренний `Prototype` на свойство prototype функции конструктора, чтобы увидеть, что они ссылаются на одно и то же.

Также для этого можно использовать свойство isPrototypeOf():

Можно использовать оператор instanceof, чтобы проверить, появляется ли свойство prototype конструктора в пределах цепочки прототипов объекта.

Итак, все объекты JavaScript имеют скрытое внутреннее свойство `Prototype` (которое можно определить с помощью __proto__ в некоторых браузерах). Объекты могут быть расширены и наследуют свойства и методы от `Prototype` их конструктора.

Прототипы складываются в цепочки, и каждый дополнительный объект наследует все по этой цепочке. Цепочка заканчивается на Object.prototype.

Семь коанов прототипа

Когда Чиро Сан спустился с горы Огненный Лис после глубокой медитации, его разум был ясным и мирным.

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

0) «Прототипом» можно назвать две разные вещи:

  • свойство прототипа, как в

  • внутреннее свойство прототипа, обозначенное как .

    Его можно получить через ES5 .

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

1) Эти концепции существуют, чтобы ответить на вопрос:

Интуитивно классическое наследование должно влиять на поиск свойств.

2)

  • используется для поиска свойств точки, как в .
  • это не используется для поиска непосредственно, только косвенно , как он определяет , при создании объекта с .

Порядок поиска:

  • свойства добавлены с помощью или
  • свойства
  • свойства и т. д.
  • если есть , верните .

Это так называемая цепочка прототипов .

Вы можете избежать поиска с помощью и

3) Есть два основных способа настройки

  • затем установил:

    Это где привыкает.

  • наборы:

4) Код:

Соответствует следующей диаграмме (некоторые элементы опущены):

На этой диаграмме показано множество предопределенных языковых узлов объектов:

  • (можно найти с помощью скобок, обязательных для соответствия синтаксису)

Наши 2 строки кода создали только следующие новые объекты:

теперь является свойством, потому что когда вы это делаете:

он оценивает с того значением , которое будет возвращать, который затем получает назначенное .

5) обычно получается через поиск:

Когда мы пишем , JavaScript выполняет поиск как:

  • не имеет
  • есть , так что возьми

Результат интуитивно правильный, поскольку используется для создания , например, установки полей, как и в классических языках ООП.

6) Классический синтаксис наследования может быть достигнут путем манипулирования цепочками прототипов.

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

Упрощенная диаграмма без всех предопределенных объектов:

Давайте посмотрим, как работает следующее:

В первой строке устанавливается значение, как объяснено в «4)».

Во второй строке, когда мы делаем:

  • находится по цепочке: -> -> ->
  • когда мы вызываем функцию в Javascript as , JavaScript автоматически становится равным внутри вызова функции!

Та же самая логика объясняет и .

В этой статье упоминаются дополнительные эффекты, о которых стоит знать. Некоторые из них могут быть недоступны без ключевого слова (TODO check which):

What to learn next

Although JavaScript may not be considered an OOP language, the use of version ES6 (because of the use of classes) will give you a feel of what it’s like to code in a more traditional OOP programming language such as C/C++. The major differences between ES5 and ES6 is the addition and clean-up of syntaxes.

There is much more to learn, such as:

  • Static methods
  • Protecting properties
  • Data encapsulation
  • Mixins
  • and more

If you’re interested in going into more detail, you can learn all the essentials with Learn OOP in JavaScript. You’ll build up to more advanced concepts such as prototypal inheritance, prototype chaining, method overriding, and mixins.

Continue reading about JavaScript and OOP

  • The Roadmap to becoming a Front-End Developer
  • Best practices for reversing a string in JavaScript, C++ & Python
  • 7 JavaScript data structures you must know

Проверка принадлежности к классу

В JavaScript есть оператор instanceof, который позволяет узнать, принадлежит ли объект к определенному классу (то есть создавал ли его определенный конструктор).

Эта проверка учитывает всю цепочку прототипного наследования:

Здесь тоже будет , так как конструктор тоже участвовал в создании объекта .

Алгоритм проверки выглядит так:

  1. Сначала свойство объекта сравнивается со свойством функции-конструктора . Если бы они были равны, это означало бы, что rabbit создан конструктором Animal. Но они не равны.
  2. Вместо берется его прототип () и теперь его свойство сравнивается с . А вот они уже равны, ведь прототип () действительно наследует напрямую от . Проверка пройдена.

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

Создание новой отдельной функции-конструктора

Проверьте себя, сможете ли вы обнаружить ошибку в коде ниже?

function Animal(name, energy) {
  this.name = name
  this.energy = energy
}

const leo = Animal('Leo', 7)

Неопытный JavaScript-разработчик может легко запутаться при анализе кода из примера выше. Как видно здесь мы используем паттерн , о котором мы уже говорили ранее. То есть, при вызове функции-конструктора нам сначала необходимо убедиться, что мы вызываем ее с использованием ключевого слова . Если мы этого не сделаем, то ключевое слово не будет ссылаться на текущий экземпляр, а присваиваемые таким образом значения свойств не будут неявно возвращаться при создании нового объекта.

В коде ниже закомментированные строки — это то, что фактически происходит «под капотом», когда мы используем при вызове функции ключевого слово .

function Animal (name, energy) {
  // const this = Object.create(Animal.prototype)

  this.name = name
  this.energy = energy

  // return this
}

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

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

function Animal (name, energy) {
  if (this instanceof Animal === false) {
    console.warn('Forgot to call Animal with the new keyword')
  }

  this.name = name
  this.energy = energy
}

Теперь вместо того, чтобы просто записывать предупреждение пользователю нашей функции, что если мы повторно вызовем функцию, но уже с ключевым словом ?

function Animal (name, energy) {
  if (this instanceof Animal === false) {
    return new Animal(name, energy)
  }

  this.name = name
  this.energy = energy
}

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

Инициализация: constructor()

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

В следующем примере конструктор устанавливает начальное значение для поля

class User{
  constructor(name){    
      this.name = name;  
  }
}

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

Внутри конструктора значение ключевого слова эквивалентно вновь созданному экземпляру (а точнее представляет собой ссылку на него).

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

class User {
  constructor(name) {
    name;     
    this.name = name; // => 'Jon Snow'
  }
}

const user = new User('Jon Snow');

Параметр внутри конструктора получает значение .

Если вы не определяете конструктор для класса, то создается конструктор по умолчанию. Конструктор по умолчанию является пустой функцией, которая при создании экземпляра никак его не изменяет.

В то же время класс JavaScript может иметь только один конструктор.

Функция-конструктор

Функции-конструкторы используются для создания объектов по определенному шаблону. Вызывать такую функцию нужно с ключевым словом new.

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

Этот объект возвращается из функции автоматически, поэтому инструкция не нужна.

Кроме того, функция-конструктор, как любая другая функция может принимать параметры.

и — это обычные объекты. У каждого из них есть свойство , равное , и свойство (имя у каждого свое).

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

Function.prototype

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

На самом деле, этот объект не совсем пустой. У него есть свойство , в котором находится ссылка на саму функцию-конструктор. Чуть позже еще поговорим о нем.

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

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

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

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

Function.prototype.constructor

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

Это нужно, чтобы у новых объектов, созданных функцией осталась информация о конструкторе, который их создал.

The __proto__ property

This points to the object which is used as a prototype.

This is the property on every object that gives it access to the property.

Every object has this property by default, which refers to the except when configured otherwise (that is, when the object’s is pointed to another prototype).

Modifying the property

This property can be modified by explicitly stating that it should refer to another prototype. The following methods are used to achieve this:

In the console, this is what you’d have:

console.log(bingo)

Notice the property and the method?

uses the argument passed to it to become the prototype.

keyword

‘s property is directed to ‘s prototype. But remember, ‘s prototype is an object (key and value pair), hence it also has a property which refers to the global protoype.

This technique is referred to as PROTOTYPE CHAINING.

Note that: the keyword approach does the same thing as but only makes it easier as it does some things automatically for you.

And so…

Every object in Javascript has access to the ‘s prototype by default. If configured to use another prototype, say , then would also have access to the Object’s prototype by default, and so on.

Object + Function Combination

You are probably confused by the fact that is a function () and it has properties accessed with a dot notation. This is referred to as a function object combination.

When functions are declared, by default they are given a lot of properties attached to it. Remember that functions are also objects in JavaScript data types.

Является ли JavaScript истинным языком ООП?

В объектно-ориентированном программировании есть три основные особенности: инкапсуляция, наследование и полиморфизм.

Инкапсуляция: инкапсуляция относится к созданию автономных модулей, которые связывают функции обработки с данными. Эти определяемые пользователем типы данных называются «классами», а один экземпляр класса является «объектом».

Наследование: это механизм, с помощью которого объект приобретает некоторые или все функции из одного или нескольких других объектов.

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

Выполнение этих требований — это то, что обычно позволяет классифицировать язык как объектно-ориентированный.

Объектно ориентирован JavaScript?

JavaScript — это язык программирования на основе прототипов. Язык программирования на основе прототипа — это стиль объектно-ориентированного программирования, который использует функции как конструкторы для классов. Хотя JavaScript имеет класс ключевых слов, он не имеет инструкции класса. Кроме того, он использует клонирование, а не наследование.

JavaScript сильно основан на объектах и является отличным языком для написания объектно-ориентированных веб-приложений. JavaScript дает вам возможность создавать собственные объекты для ваших собственных приложений. Что касается инкапсуляции, объекты JavaScript — это сущности, поддерживающие данные и функции, но у них нет расширенной встроенной поддержки для скрытия внутренних деталей. С вашими объектами вы можете закодировать события, которые срабатывают, когда вы хотите, и код инкапсулирован. Его можно инициализировать в любое количество раз. Объекты JavaScript не заботятся о конфиденциальности

Все свойства и методы являются общедоступными, если не принимать никаких мер предосторожности. Кроме того, в JavaScript мы можем реализовать различные типы полиморфизма несколькими способами, и, возможно, мы сделали это много раз неосознанно

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

Прототипы в JavaScript

В мануале Объекты в JavaScript вы уже ознакомились с объектами и научились создавать их, изменять и извлекать их свойства. Теперь вы научитесь использовать прототипы для расширения объектов.

Каждый объект в JavaScript имеет внутреннее свойство, называемое `Prototype`. Для примера попробуйте создать новый пустой объект.

Так создается объект обычно, но есть и другой способ сделать это – с помощью конструктора объекта: let x = new Object().

Примечание: Двойные квадратные скобки в `Prototype` означают, что свойство является внутренним и не может быть доступно непосредственно в коде.

Чтобы найти свойство `Prototype` этого нового объекта, нужно использовать метод getPrototypeOf ().

Вывод будет состоять из нескольких встроенных свойств и методов.

Еще один способ найти `Prototype` – это свойство __proto__, которое предоставляет внутренний `Prototype` объекта.

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

Это вернет такой же результат, что и getPrototypeOf().

Важно, чтобы каждый объект JavaScript имел `Prototype`, поскольку он позволяет связать два и более объекта. Созданные вами объекты имеют `Prototype` так же, как и встроенные объекты, такие как Date и Array

Сослаться на это внутреннее свойство можно с помощью свойства prototype

Созданные вами объекты имеют `Prototype` так же, как и встроенные объекты, такие как Date и Array. Сослаться на это внутреннее свойство можно с помощью свойства prototype.

Subclassing

This is a feature in OOP where a class inherits features from a parent class but possesses extra features which the parent doesn’t.

The idea here is, for example, say you want to create a cats class. Instead of creating the class from scratch — stating the name, age and species property afresh, you’d inherit those properties from the parent animals class.

This cats class can then have extra properties like color of whiskers.

Let’s see how subclasses are done with .

Here, we need a parent which the subclass inherits from. Examine the following code:

With the above, we get the following outputs:

When you log the contents of clara out in the console, we have:

console.log(clara)

You’ll notice that has a property which references the constructor and gets access to the method. This property also has a property which references the constructor thereby getting access to and . and are properties that exist on every object created from this.

Using the method approach, the above translates to:

is a method which takes in two arguments — the object (first argument) and the desired prototype (second argument).

From the above, the function returns an object with the as prototype. The function returns an object with as it’s prototype. on the other hand, is given a prototype of .

Therefore, ordinary animals only have access to the but cats have access to the and the .

Функции-конструкторы

Функции-конструкторы – это функции, которые используются для построения новых объектов. Оператор new используется для создания новых экземпляров на основе функции конструктора. Вы уже знаете некоторые встроенные конструкторы JavaScript (new Array() и new Date(), например); вы также можете создавать собственные пользовательские шаблоны для построения объектов.

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

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

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

Теперь у вас есть функция-конструктор Hero с двумя параметрами: name и level. Поскольку у каждого персонажа будет имя и уровень, для них имеет смысл наследовать эти свойства. Ключевое слово this будет ссылаться на новый созданный экземпляр; this.name в параметре name гарантирует, что новый объект будет иметь свойство name.

Создайте новый экземпляр с помощью new.

Если запросить в консоли hero1, вы увидите новый объект с правильно установленными свойствами:

Теперь, если запросить `Prototype` объекта hero1, вы увидите constructor Hero().

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

Мы можем добавить помощью prototype. Создайте метод greet().

Поскольку greet() – это prototype в Hero, а hero1 является экземпляром Hero, метод будет доступен и для hero1:

Если вы проверите `Prototype` в Hero, вы увидите доступную опцию greet().

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

С помощью метода call() скопируйте свойства одного конструктора в другой. Создайте конструкторы Warrior и Healer.

Оба новых конструктора теперь обладают свойствами Hero и несколькими уникальными свойствами. Добавьте метод attack() в Warrior и метод heal() в Healer.

Теперь можно создать персонажей с двумя новыми доступными классами:

Теперь hero1 распознается как Warrior с новыми свойствами.

Можно использовать новые методы, установленные в прототипе Warrior.

Но что произойдет, если попробовать использовать следующие методы в цепочке прототипов?

Свойства и методы прототипа не связываются автоматически, когда вы используете call() для создания цепочек. Используйте Object.create(), чтобы связать прототипы, прежде чем создавать и добавлять какие-либо дополнительные методы к прототипу.

Теперь можно использовать методы прототипа из Hero в экземплярах Warrior или Healer.

Вот полный код страницы создания персонажа.

В этом файле вы создали класс Hero с базовыми свойствами, два класса персонажей – Warrior и Healer – из исходного конструктора, добавили методы в прототипы и создали отдельные экземпляры персонажей.

Выводы

Классы JavaScript инициализируют свои экземпляры конструкторами, определяя их методы, поля, а также их начальные значения. Вы можете прикрепить поля и методы к объявлению класса, используя ключевое слово .

Механизм наследования реализуется с использованием ключевого слова , с помощью которого вы можете легко создать дочерний класс от родительского. Ключевое слово используется для доступа к полям и методам родительского класса из дочернего.

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

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

Рейтинг
( Пока оценок нет )
Editor
Editor/ автор статьи

Давно интересуюсь темой. Мне нравится писать о том, в чём разбираюсь.

Понравилась статья? Поделиться с друзьями:
Люкс-хост
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: