Прототипы объектов в JavaScript

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

Прототип — это другой объект, от которого текущий объект может наследовать свойства и методы. Это позволяет создавать иерархии объектов.

Все объекты JavaScript наследуют свойства и методы от прототипа.

Создаем объект и вызываем методы

Перед продолжением вспоминаем:

  • метод — это функция, которая хранится внутри объекта;
  • ключевое слово this — ссылка на объект.

Создадим простой объект person, name и age — свойства, sayName() — метод.

const person = {
  name: "Сергей",
  age: 25,
  sayName: function () {
    console.log(`Меня зовут ${this.name}`);
  },
};

person.sayName(); // Метод sayName() хранится в объекте person

В последней строке вызывается метод объекта sayName() — в консоли отобразилась ожидаемая фраза «Меня зовут Сергей»:

Метод объекта person sayName вывел в консоль браузера фразу "Меня зовут Сергей"

Попробуем вызвать несуществующий метод объекта person sayAge():

const person = {
  name: "Сергей",
  age: 25,
  sayName: function () {
    console.log(`Меня зовут ${this.name}`);
  },
};

person.sayAge(); // Метода sayAge() у объекта person нет

Поскольку метода sayAge() в объекте person не существует, вылетает ошибка «Uncaught TypeError: person.sayAge is not a function».

Вызов несуществующего метода sayAge() приводит к ошибке: Uncaught TypeError: person.sayAge is not a function
Пока всё понятно — вызов несуществующего метода sayAge() приводит к ошибке

Теперь давайте используем метод toString(), которого тоже нет у объекта person:

const person = {
  name: "Сергей",
  age: 25,
  sayName: function () {
    console.log(`Меня зовут ${this.name}`);
  },
};

console.log(person.toString());

Удивительно, но ошибки нет:

Разгадка заключается в том, что объект person позаимствовал метод toString() у прототипа.

В JavaScript у каждого объекта есть прототип, который можно рассматривать как его родительский объект. Когда вы обращаетесь к свойству объекта и не находите его, JavaScript начнет поиск этого свойства в прототипе объекта. Если нужное свойство не найдено и в прототипе, поиск будет продолжаться в прототипе прототипа и так далее. В конце концов, поиск дойдет до Object.prototype, который является последним в этой цепочке.

Дополнительно рекомендую изучить тему «Встроенные прототипы».

Прототипы

Ссылка на прототип объекта хранится в специальном свойстве, называемом [[Prototype]]. Это свойство не отображается явно в коде и является скрытым. Однако его можно увидеть в инструментах разработчика браузера, например, в консоли, что тает возможность изучить структуру объектов и их прототипов.

Шаг 1. Выводим объект person в консоль браузера:

const person = {
  name: "Сергей",
  age: 25,
  sayName: function () {
    console.log(`Меня зовут ${this.name}`);
  },
};

console.log(person);

Шаг 2. Смотрим, какие методы есть у прототипа:

Только что созданный объект person получил по наследству от прототипа множество методов, включая toString()

Метод Object.getPrototypeOf() возвращает прототип (то есть, внутреннее свойство [[Prototype]]) указанного объекта.1

Object.getPrototypeOf(person);

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

Рассмотрим пример, который продемонстрирует прототипное наследование. Используем уже знакомый базовый объект person, а затем создадим более специфический объект student, который будет наследовать свойства и методы от person, добавляя некоторые свои уникальные свойства и методы.

Ниже будет использовать метод Object.create(), поэтому немного теории:

Object.create() — метод используется для создания нового объекта с указанным прототипом и, опционально, с заданными свойствами.

const person = {
  name: "Сергей",
  age: 25,
  sayName: function () {
    console.log(`Меня зовут ${this.name}`);
  },
};

// Создаем объект student, который наследует от person
const student = Object.create(person); // Создаем новый объект student с прототипом person
student.study = function () {
  console.log(`${this.name} учится на ${this.subjects}`);
};

// Устанавливаем уникальные свойства для student
student.name = "Алексей"; // Изменяем имя
student.age = 20; // Изменяем возраст
student.subjects = "программиста"; // Добавляем, на кого учится студент

// Используем методы из person и student
student.sayName(); // "Меня зовут Алексей"
student.study(); // "Алексей учится на программиста."

// Проверяем доступ к свойствам объекта person
console.log(`${student.name} - ${student.age} лет`); // "Алексей - 20 лет"

// Проверяем доступ к методу toString()
console.log(student.toString()); // [object Object]

// Выводим объект student в консоль
console.log(student); 

Методы toString(), hasOwnProperty() и другие, находятся в объекте Object.prototype, который является конечным прототипом в цепочке прототипов. Это значит, что если вы попытаетесь получить доступ к этим методам у любого объекта, который не имеет своих собственных реализаций, JavaScript будет искать их в Object.prototype.

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

Объект без [[Prototype]]

Можно создать объект без свойства [[Prototype]], используя уже знакомый метод Object.create(), прописав в качестве обязательного аргумента null:

const nemo = Object.create(null);

nemo.name = "Капитан Немо";

console.log(nemo);
У объекта nemo нет внутреннего свойства [[Prototype]], соответственно, нет ссылки на другой объект
У объекта nemo нет внутреннего свойства [[Prototype]], соответственно, нет ссылки на другой объект

Объект, созданный с помощью Object.create(null), не имеет методов, свойств и прототипа, которые доступны в обычных объектах, что делает его «чистым» объектом.

Попробуем использовать метод toString(), который исправно отрабатывал во всех предыдущих примерах:

const nemo = Object.create(null);

nemo.name = "Капитан Немо";

console.log(nemo.toString());
Вылетела ошибка, так как нет доступа к Object.prototype, в котором хранится метод toString()
Вылетела ошибка, так как нет доступа к Object.prototype, в котором хранится метод toString()

Чтобы заставить исправно работать метод toString() в примере выше, можно установить ему прототип Object.prototype. Для этого используем современный метод Object.setPrototypeOf()2:

const nemo = Object.create(null);

nemo.name = "Капитан Немо";

Object.setPrototypeOf(nemo, Object.prototype);

console.log(nemo.toString());

console.log(nemo);
Метод toString() отработал без ошибок, так объекту nemo был установлен прототип Object.prototype
Метод toString() отработал без ошибок, так объекту nemo был установлен прототип Object.prototype

Свойство __proto__

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

Рекомендуемые альтернативы:

  • Для получения прототипа объекта используйте Object.getPrototypeOf(obj).
  • Для установки прототипа используйте Object.setPrototypeOf(obj, prototype).

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

  1. MDN Object.getPrototypeOf() ↩︎
  2. MDN Object.setPrototypeOf() ↩︎

Оставьте первый комментарий

Оставить комментарий

Ваш электронный адрес не будет опубликован.


*