Для полноценного изучения ООП в JavaScript нужно разобраться с таким понятием как контекст this
. На базе контекста строится почти все остальное, включая то, как работают методы и классы.
Немного усложняет ситуацию то, что контекст по-разному работает для обычных и стрелочных функций. И так как стрелочные функции появились в языке позже, то для понимания темы нужно начать изучение контекста с обычных функций. Небольшое напоминание про то, как выглядят такие функции:
// Определение стрелочной функции и присваивание константе
const f = () => 'i am an arrow function';
// Определение обычной анонимной функции
function() {
return 'i am a regular function without name';
}
// Определение обычной именованной функции
function f() {
return 'i am a regular function with name';
}
В этом уроке мы рассматриваем только обычные функции, стрелочные будут в одном из следующих уроков. Обычные функции работают с контекстом одинаково, независимо от того, именованные они или нет.
В JavaScript функции ведут себя как данные: их можно записывать в переменные, константы и даже в свойства объектов. Функции, записанные внутрь свойств объектов, называют методами:
const company = { name: 'Hexlet' };
// Создание функции, которая сразу же присваивается свойству getName и становится методом
company.getName = function() {
return 'Hexlet';
};
// Вызов метода
company.getName(); // "Hexlet"
Это всего лишь один из множества возможных вариантов добавления функции в объект. Ниже еще несколько примеров:
// При создании объекта
const obj = {
getName: function() {
return 'Hexlet';
},
};
// Через присваивание константы
const company = { name: 'Hexlet' };
function getHexlet() {
return 'Hexlet';
};
// Имя не принципиально
company.getName = getHexlet;
company.getName(); // "Hexlet"
Все варианты выше эквивалентны. Они приводят к одному и тому же результату, но есть одна загвоздка. Метод возвращает строку и никак не использует данные объекта. Если поменяется имя, то метод продолжит возвращать "зашитое" в него значение, а не текущее имя компании внутри объекта.
company.getName(); // "Hexlet"
company.name = 'Hexlet Plus';
// Имя поменяли, но очевидно, что возврат остался прежний
company.getName(); // "Hexlet"
Для решения этой задачи, нам нужно получить доступ к данным объекта внутри метода. Делается это через специальное ключевое слово this
, называемое контекстом. Внутри методов оно ссылается на текущий объект, к которому привязан метод.
const company = { name: 'Hexlet', employees: [] };
company.getName = function getName() {
return this.name;
};
company.getName(); // "Hexlet"
company.name = 'Hexlet Plus';
company.getName(); // "Hexlet Plus"
this
дает возможность не только читать данные, но и менять их:
company.setName = function setName(name) {
this.name = name;
};
company.getName(); // "Hexlet"
company.setName('Hexlet Plus');
company.getName(); // "Hexlet Plus"
Другой пример — изменение внутреннего массива в объекте:
// Добавление нового сотрудника
company.addEmployee = function addEmployee(user) {
// Важно, что на момент вызова, employees уже добавлен в company
this.employees.push(user);
};
const user = { name: 'Petya' };
company.addEmployee(user);
company.employees; // [{ name: 'Petya' }]
// Или через метод
company.getEmployees = function() {
return this.employees;
};
company.getEmployees(); // [{ name: 'Petya' }]
Как видно из примеров выше, свойства можно менять как напрямую, так и из методов. Какой способ предпочесть – зависит от ситуации. С дальнейшим прохождением курсов и опытом вы начнете лучше понимать, какой способ предпочесть.
Контекст
Выше, когда давалось определение this
, говорилось, что this
ссылается на текущий объект, к которому привязан метод. И здесь кроется ключевое отличие this
в JavaScript от this
в других языках. В JavaScript this
у метода может измениться:
const company1 = { name: 'Hexlet', getName: function getName() { return this.name } };
const company2 = { name: 'Hexlet Plus' };
company1.getName(); // "Hexlet"
company2.getName = company1.getName;
// В обоих случаях одна и та же функция
company2.getName(); // "Hexlet Plus"
company1.getName(); // "Hexlet"
Что здесь произошло? Вызов той же самой функции из другого объекта привел к смене объекта, на который ссылается this
. Эта особенность называется поздним связыванием. Значение this
ссылается на тот объект, из которого происходит вызов метода.
Лучше всего можно понять эту особенность, познакомившись с тем, как вызываются функции внутри самого JavaScript и откуда там берется this
. Так как в JavaScript функции — это тоже объекты, то у них есть свои методы. Среди них есть метод call()
, который и используется для вызова:
const sayHi = () => 'Hi!';
sayHi.call(); // "Hi!"
Зачем так сделано? Дело в том, что первым параметром call()
принимает контекст - объект, на который и будет ссылаться this
внутри функции. Функции для этого не обязательно быть методом:
const getName = function getName() {
return this.name;
};
const company1 = { name: 'Hexlet' };
// Функция вызывается напрямую, она не является методом
getName.call(company1); // "Hexlet"
const company2 = { name: 'Hexlet Plus' };
getName.call(company2); // "Hexlet Plus"
В примере выше мы заменили контекст функции getName()
с помощью call()
, передав в него новый контекст.
В этом и заключается весь секрет this
. Это ссылка на контекст, который мы можем заменить в функции, как показано выше. Также JavaScript прокидывает контекст автоматически в метод. В этом случае можно точно сказать, на какой объект ссылается контекст. Например, в коде ниже метод getName()
принадлежит объекту company
и контекст ссылается на этот же объект:
const company = { name: 'Hexlet', getName: function getName() { return this.name } };
Теперь, когда вы знаете как работает this
, попробуйте ответить на вопрос, что будет выведено на экран?
const company = {
name: 'Hexlet',
country: {
name: 'Finland',
getName: function getName() {
return this.name;
}
},
};
console.log(company.country.getName()); // => ?
Правильный ответ: "Finland"
. Почему? Потому что контекстом для метода getName()
является объект country
, а не company
. Если немного модифицировать код, то понять эту идею станет проще:
const { country } = company;
console.log(country.getName()); // "Finland"
Сокращенное определение методов
Из-за необходимости использовать обычные функции при создании объектов в JavaScript был введен специальный сокращенный синтаксис создания методов при определении объектов:
const company = {
name: 'Hexlet',
getName() {
return this.name;
},
// То же самое что
// getName: function getName() {
// return this.name;
// },
};
Такой способ – всего лишь "синтаксический сахар". Он позволяет сделать запись чуть короче, но не меняет поведение. Главное запомните – это обычная функция, а не стрелочная. В дальнейшем мы будем использовать именно такое определение.
Дополнительные материалы

Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
- Статья «Как учиться и справляться с негативными мыслями»
- Статья «Ловушки обучения»
- Статья «Сложные простые задачи по программированию»
- Урок «Как эффективно учиться на Хекслете»
- Вебинар «Как самостоятельно учиться»
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.