Зарегистрируйтесь для доступа к 15+ бесплатным курсам по программированию с тренажером

Конструктор JS: Введение в ООП

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

Возьмем понятие "компания" и построим абстракцию вокруг него без использования инкапсуляции:

// Реальное устройство будет значительно сложнее
// файл: company.js

// Конструктор (в общем смысле этого слова)
const make = (name, website) => {
  return { name, website };
};

// Селекторы
const getName = (company) => company.name;
const getWebsite = (company) => company.website;

Теперь использование:

import { make, getName } from './company.js';

const company = make('Hexlet', 'https://hexlet.io');
console.log(getName(company)); // Hexlet

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

// Реальное устройство будет значительно сложнее
// файл: company.js

const make = (name, website) => {
  return {
    name,
    website,
    getName() {
      return this.name;
    },
    getWebsite() {
      return this.website;
    },
  };
};

И использование:

import { make } from './company.js';

const company = make('Hexlet', 'https://hexlet.io');
console.log(company.getName()); // Hexlet

Здесь мы видим несколько удобных моментов по сравнению с вариантом на функциях:

  • Нам больше не надо импортировать getName, он уже содержится внутри компании.
  • Можно пользоваться автодополнением кода.

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

Перепишем наш пример, избежав постоянного создания методов:

// Не забываем что нам нужны обычные, а не стрелочные функции!

function getName() {
  return this.name;
};

function getWebsite() {
  return this.website;
};

// С точки зрения использования ничего не поменялось, но зато перестали копироваться функции.
const make = (name, website) => {
  return {
    name,
    website,
    getName,
    getWebsite,
  };
};

Оператор new

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

// Такую функцию принято называть конструктором (хотя технически это обычная функция с контекстом)
// Конструкторы пишутся с заглавной буквы
function Company(name, website) {
  this.name = name;
  this.website = website;
  // Методы по прежнему определены снаружи как обычные функции
  this.getName = getName;
  this.getWebsite = getWebsite;
}

Теперь использование:

const company = new Company('Hexlet', 'https://hexlet.io');
console.log(company.getName()); // Hexlet

Самое интересное в этом примере – оператор new (как и многое в js, он работает не так как new в других языках). Фактически он создает объект, устанавливает его как контекст во время вызова конструктора (в данном случае Company) и возвращает созданный объект. Именно поэтому сам конструктор ничего не возвращает (хотя может, но это другой разговор), а внутри константы company оказывается нужный нам объект.

// Упрощенная иллюстрация работы new внутри интерпретатора при таком вызове:
// new Company();

const obj = {};
Company.bind(obj)(name, website); // этот вызов просто наполнил this (равный obj) нужными данными
return obj;

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

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

// Специальный синтаксис создания массивов
// Массивы это объекты, вспомните свойство length
const numbers = [10, 3, -3, 0]; // литерал

// Объектный способ создания через конструктор
// Результат ниже эквивалентен тому что происходит выше
const numbers = new Array(10, 3, -3, 0);

// У дат нет литералов, они создаются как объекты
const date = new Date('December 17, 1995 03:24:00');
// У дат очень много методов
date.getMonth(); // 11, в JS месяцы нумеруются с нуля

// Так можно создавать даже функции
// Последний аргумент это тело, все предыдущие – аргументы
const sum = new Function('a', 'b', 'return a + b');
sum(2, 6); // 8

Но не все функции могут быть конструкторами. Отсутствие своего контекста делает невозможным использование оператора new вместе со стрелочными функциями:

const f = () => {};
// TypeError: function is not a constructor
const obj = new f();

Дополнительные материалы

  1. Natives

Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты.

Ошибки, сложный материал, вопросы >
Нашли опечатку или неточность?

Выделите текст, нажмите ctrl + enter и отправьте его нам. В течение нескольких дней мы исправим ошибку или улучшим формулировку.

Что-то не получается или материал кажется сложным?

Загляните в раздел «Обсуждение»:

  • задайте вопрос. Вы быстрее справитесь с трудностями и прокачаете навык постановки правильных вопросов, что пригодится и в учёбе, и в работе программистом;
  • расскажите о своих впечатлениях. Если курс слишком сложный, подробный отзыв поможет нам сделать его лучше;
  • изучите вопросы других учеников и ответы на них. Это база знаний, которой можно и нужно пользоваться.
Об обучении на Хекслете

Для полного доступа к курсу нужна профессиональная подписка

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

Получить доступ
900
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

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

  • 120 курсов, 2000+ часов теории
  • 900 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы соглашаетесь c «Политикой конфиденциальности» и «Условиями оказания услуг»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff
Рекомендуемые программы

С нуля до разработчика. Возвращаем деньги, если не удалось найти работу.

Иконка программы Фронтенд-разработчик
Профессия
Разработка фронтенд-компонентов веб-приложений
29 сентября 8 месяцев
Иконка программы Node.js-разработчик
Профессия
Разработка бэкенд-компонентов веб-приложений
29 сентября 8 месяцев

Есть вопрос или хотите участвовать в обсуждении?

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы соглашаетесь c «Политикой конфиденциальности» и «Условиями оказания услуг»