Работа мечты в один клик 💼

💭Мечтаешь работать в Сбере, но не хочешь проходить десять кругов HR-собеседований? Теперь это проще, чем когда-либо!
💡AI-интервью за 15 минут – и ты уже на шаг ближе к своей новой работе.
Как получить оффер? 📌 Зарегистрируйся 📌 Пройди AI-интервью 📌 Получи обратную связь сразу же!
HR больше не тянут время – рекрутеры свяжутся с тобой в течение двух дней! 🚀
Реклама. ПАО СБЕРБАНК, ИНН 7707083893. Erid 2VtzquscAwp
JavaScript – это не классический объектно-ориентированный язык, основанный на классах, однако в нем есть способ для их реализации.
Согласно Википедии класс-ориентированное программирование – это стиль объектно-ориентированного программирования (ООП), в котором наследование происходит через определение классов объектов, а не через сами объекты.
Классовая модель – это самая популярная реализация объектно-ориентированного программирования. Однако ООП в JavaScript основано не на классах, а на прототипах. В этой модели для создания новых объектов используется шаблонный объект – прототип.
Посмотрим на пример кода:
let names = {
fname: "Dillion",
lname: "Megida"
}
console.log(names.fname); // Dillion
console.log(names.hasOwnProperty("mname")); // false
В переменную names
мы записали объект, у которого есть два свойства – fname
и lname
– и ни одного метода. Откуда же взялся метод hasOwnProperty
?
Дело в том, что объект names не существует сам по себе, у него есть прототип – Object.prototype
.
Выведем эту переменную в консоль:
console.log(names);
Вот что мы увидим:

Развернем загадочное свойство __proto__
:

Мы можем увидеть конструктор объекта names
– функцию Object()
– и множество методов под ним. Среди них – и hasOwnProperty
. Все эти методы хранятся в прототипе Object
, но доступны и самому объекту names
.
Другими словами, все объекты в JavaScript создаются с использованием прототипа Object.prototype
:
names.__proto__ === Object.prototype; // true
И каждый объект имеет доступ ко всем методам этого прототипа, не обладая ими напрямую.
Свойство __proto__
Каждый объект в JavaScript имеет свойство __proto__
, в котором хранится ссылка на другой объект, являющийся его прототипом.
Через это свойство, этот объект и получает доступ к свойствам и методам прототипа.
По умолчанию у всех объектов в свойство __proto__
записана ссылка на Object.prototype
. Однако мы можем изменить это – иначе говоря «унаследовать» объект от другого прототипа.
Изменение прототипа
Не следует менять свойство __proto__
напрямую, для этого существуют специальные методы.
Object.create()
function DogObject(name, age) {
let dog = Object.create(constructorObject);
dog.name = name;
dog.age = age;
return dog;
}
let constructorObject = {
speak: function(){
return "I am a dog"
}
}
let bingo = DogObject("Bingo", 54);
console.log(bingo);
Посмотрим на объект bingo
в консоли:

Теперь в свойстве __proto__
находится другая функция-конструктор и метод speak
.
Object.create
создает новый объект, автоматически устанавливая ему указанный прототип.
Ключевое слово new
function DogObject(name, age) {
this.name = name;
this.age = age;
}
DogObject.prototype.speak = function() {
return "I am a dog";
}
let john = new DogObject("John", 45);
john.__proto__ === DogObject.prototype; // true
Теперь свойство john.__proto__
ссылается на DogObject.prototype
. Обратите внимание – не на функцию DogObject
(она является только конструктором объектов), а на объект, записанный в ее свойстве prototype
.
Данные и методы хранятся только в объектах. Функции-конструкторы вродеDogObject
илиObject
только предлагают удобный способ конфигурации (через this) и создания новых объектов.Функция-конструктор имеет свойствоprototype
, в котором хранится объект-прототип. По его образу эта функция будет создавать новые объекты.
Прототип объекта, хранящийся в его свойстве__proto__
, и объект, хранящийся в свойствеprototype
конструктора – одно и то же.
В свою очередь прототип прототипа john
ссылается на уже знакомый нам Object.prototype
.
DogObject.prototype.__proto__ === Object.prototype;
То, что мы видим, называется цепочкой прототипов. Это основа прототипно-ориентированного программирования. Объект может получить доступ к любому свойству или методу, которое есть у любого звена его прототипной цепочки.
Сколько бы прототипов ни было в этой цепочке, последний почти всегда будет наследовать от исходногоObject.prototype
. При необходимости можно избавиться от дефолтного прототипа, вызвав методObject.create
и передав емуnull
. Это бывает полезно для создания словарей, в которых не должно быть ничего лишнего.
Ключевое слово new
, которое превращает обычную функцию в конструктор объектов, делает по сути ту же самую вещь, что и Object.create()
, только проще.
Функции – тоже объекты
Если вас смутило наличие свойств у функции DogObject
, доступных через точку (prototype
), то вспомните, что в JS функции – это тоже объекты.
В качестве прототипа они имеют объект Function.prototype
, который в свою очередь наследует от Object.prototype
.
В Function.prototype содержится много дополнительных свойств и методов, которые наследует от него любая функция (call
, apply
, isPrototypeOf
).
Переходим к классам
Прототипная реализация ООП интересна и имеет свои преимущества, но она не так популярна, как классовая. Поэтому когда в ECMAScript 2015 появилось ключевое слово class
, разработчики были очень этому рады.
JS стал походить на классический привычный объектно-ориентированный язык. Однако не будем забывать, что классы в нем – всего лишь синтаксический сахар над теми самыми прототипами. На вид – классы, под капотом – по-прежнему прототипы.
Вот так выглядит обычное создание класса в JavaScript:
class Animals {
constructor(name, specie) {
this.name = name;
this.specie = specie;
}
sing() {
return `${this.name} can sing`;
}
dance() {
return `${this.name} can dance`;
}
}
let bingo = new Animals("Bingo", "Hairy");
console.log(bingo);
Посмотрим в консоль:

Ничего не напоминает?
Все то же свойство __proto__
, которое ссылается на Animals.prototype
(-> Object.prototype).
Мы видим, что собственные значения свойств (name
и specie
) определяются внутри метода constructor
. Кроме него создаются дополнительные функции sing
и dance
– методы прототипа.
Подкапотную реализацию этой конструкции мы разбирали только что – это функция-конструктор + ключевое слово new
.
function Animals(name, specie) {
this.name = name;
this.specie = specie;
}
Animals.prototype.sing = function(){
return `${this.name} can sing`;
}
Animals.prototype.dance = function() {
return `${this.name} can dance`;
}
let Bingo = new Animals("Bingo", "Hairy");
Субклассирование
Субклассирование – это наследование и расширение родительского класса дочерним.
Предположим, вам нужно создать класс Cats
. Вы могли бы написать его с нуля, но зачем, если уже есть класс Animals
, который реализует часть нужного вам функционала. Вы можете просто унаследовать Cats
от Animals
и добавить то, чего не хватает (например, цвет усов).
Наследование в класс-ориентированном синтаксисе выглядит так:
class Animals {
constructor(name, age) {
this.name = name;
this.age = age;
}
sing() {
return `${this.name} can sing`;
}
dance() {
return `${this.name} can dance`;
}
}
class Cats extends Animals {
constructor(name, age, whiskerColor) {
super(name, age);
this.whiskerColor = whiskerColor;
}
whiskers() {
return `I have ${this.whiskerColor} whiskers`;
}
}
let clara = new Cats("Clara", 33, "indigo");
Объект класса Cats
clara
может использовать свойства и методы как класса Cats
, так и класса Animals
.
console.log(clara.sing()); // "Clara can sing"
console.log(clara.whiskers()); // "I have indigo whiskers"
Вот так clara
выглядит в консоли:

В свойстве clara.__proto__.constructor
лежит класс Cats
, через него осуществляется доступ к методу whiskers()
. Дальше в цепочке прототипов – класс Animals
, с методами sing()
и dance()
. name
и age
– это свойства самого объекта.
Перепишем этот код в прототипном стиле с использованием метода Object.create()
:
function Animals(name, age) {
let newAnimal = Object.create(animalConstructor);
newAnimal.name = name;
newAnimal.age = age;
return newAnimal;
}
let animalConstructor = {
sing: function() {
return `${this.name} can sing`;
},
dance: function() {
return `${this.name} can dance`;
}
}
function Cats(name, age, whiskerColor) {
let newCat = Animals(name, age);
Object.setPrototypeOf(newCat, catConstructor);
newCat.whiskerColor = whiskerColor;
return newCat;
}
let catConstructor = {
whiskers() {
return `I have ${this.whiskerColor} whiskers`;
}
}
Object.setPrototypeOf(catConstructor, animalConstructor);
const clara = Cats("Clara", 33, "purple");
clara.sing(); // "Clara can sing"
clara.whiskers(); // "I have purple whiskers"
Метод Object.setPrototypeOf
принимает два аргумента (объект, которому нужно установить новый прототип, и собственно сам желаемый прототип).
Функция Animals
возвращает объект, прототипом которого является animalConstructor
. Функция Cats
создает объект с помощью конструктора Animals
, но принудительно меняет его прототип на catConstructor
, добавляя таким образом новые свойства. catConstructor
в свою очередь тоже получает прототипом animalConstructor
, чтобы образовалась цепочка прототипного наследования.
Таким образом, обычные животные будут иметь доступ только к методам animalConstructor
, а кошки – еще и к catConstructor
.
JavaScript оказался достаточно гибок, чтобы превратить свое прототипное ООП в классовое для удобства разработчиков.
Комментарии