Привет, Хабр. Я Саша, разработчик, пишу на JS. Ранее я рассказывал о callback-функциях, деструктуризации, операторах и многом другом. Если вы уже успели познакомиться с основами JavaScript, то наверняка вам знакомы такие понятия, как объекты и функции.

В этой статье мы двинемся дальше и соберем эти знания воедино. Я расскажу вам о методах объектов и загадочном слове this. Разберемся, для чего они нужны, как сделать объекты по-настоящему живыми и как избежать частых ошибок. Ну что, начнем. 

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

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

Методы объектов 

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

const product = {
 name: "Кофе",
 price: 600,
 count: 2,
};

function getTotalPrice(product) {
 return product.price * product.count;
}

console.log(getTotalPrice(product));
Результат в консоли.
Результат в консоли.

Что здесь происходит

У нас есть объект product, который хранит данные о товаре. Отдельно от него существует функция getTotalPrice, которая для расчетов должна явно получить этот объект. А сама логика расчета тесно связана с объектом, но формально находится снаружи.

Какие здесь минусы

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

  • Неудобство использования. Каждый раз для расчета нужно передавать объект в функцию.

  • Хрупкость. Если структура объекта изменится (например, свойство count переименуется в quantity), придется исправлять все функции, которые с ним работают.

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

const product = {
 name: "Кофе",
 price: 600,
 count: 2,

 getTotalPrice: function () {
   return product.price * product.count;
 }
};

console.log(product.getTotalPrice(product));
Результат в консоли тот же.
Результат в консоли тот же.

Что же изменилось? Функция getTotalPrice теперь находится внутри объекта product. Она стала его неотъемлемой частью. А для ее вызова мы обращаемся к самому объекту через точку: product.getTotalPrice(). Так мы подходим к главному термину.

Метод — это функция, являющаяся свойством объекта. Если свойство хранит функцию, оно называется методом этого объекта.

Чем методы отличаются от обычных функций

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

Вы наверняка заметили странность в коде метода. Да, функция теперь внутри объекта, но для доступа к данным (product.price) она по-прежнему использует внешнюю переменную product:

getTotalPrice: function () {
  return product.price * product.count;
}

Этот подход очень ненадежен. Почему это проблема:

  • если мы переименуем константу product в coffee, метод сразу сломается;

  • если этот объект будет элементом массива, у него не будет внешнего имени product;

  • при передаче объекта в другую функцию мы теряем его исходное имя.

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

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

Бесплатный базовый курс по JS

Рассказываем, как работать с переменными, типами данных, функциями и о многом другом!

Начать изучение →

Что такое this

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

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

this позволяет методу получить доступ к данным своего объекта, не зная и не используя его внешнее имя. Метод становится переносимым и независимым. Он говорит: «Возьми данные у того объекта, который меня вызвал (у моего this), а не ищи какую-то внешнюю переменную product».

Перепишем наш код с использованием this:

const product = {
 name: "Кофе",
 price: 600,
 count: 2,

 getTotalPrice: function () {
   return this.price * this.count;
 }
};

console.log(product.getTotalPrice());
Результат в консоли тот же.
Результат в консоли тот же.

Что изменилось?

  • Вместо product.price мы пишем this.price.

  • Вместо product.count мы пишем this.count.

Когда мы вызываем метод как product.getTotalPrice(), JavaScript понимает, что функция была вызвана в контексте объекта product. Поэтому внутри этой функции слово this автоматически становится ссылкой на объект product.

Мы выяснили, что this — это удобный способ обратиться к самому объекту внутри метода, не привязываясь к его внешнему имени. Теперь давайте познакомимся с ключевой особенностью использования this.

Главное правило — this определяется в момент вызова. Значение this не фиксировано. Оно определяется не там, где функция объявлена, а тем, как она была вызвана.

В нашем примере this работает предсказуемо, потому что мы вызываем метод через точку — product.getTotalPrice(). В этом случае this внутри метода и есть product. Но если метод присвоить в константу как ссылку, то this внутри него уже не будет содержать ссылку на этот объект: 

const product = {
 name: "Кофе",
 price: 600,
 count: 2,

 getTotalPrice: function () {
   return this.price * this.count;
 }
};

const getTotalPrice2 =  product.getTotalPrice;

console.log(getTotalPrice2());
Результат в консоли.
Результат в консоли.

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

Практика для закрепления

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

// Функция для создания товаров
function createProduct(name, price, count) {
 return {
   name: name,
   price: price,
   count: count,

   getTotalPrice: function() {
     return this.price * this.count;
   },

   getInfo: function() {
     return ${this.name} | ${this.count} шт. | ${this.getTotalPrice()} руб.;
   }
 };
}

// Создаем массив товаров
const products = [
 createProduct("Кофе", 600, 2),
 createProduct("Чай", 300, 3),
 createProduct("Печенье", 150, 5),
 createProduct("Шоколад", 200, 1)
];

// Выводим информацию о каждом товаре в консоль
for (const product of products) {
 console.log(product.getInfo());
}
Результат в консоли.
Результат в консоли.

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

Итог

Сегодня мы сделали важный шаг в изучении JavaScript. Давайте тезисно подчеркнем то, что узнали:

  • методы — это функции внутри объектов, которые делают наши данные живыми;

  • this — это способ метода обратиться к своему объекту без привязки к внешнему имени.

Не переживайте, если тема кажется сложной, уверенность придет с  практикой. Экспериментируйте, пишите код! Попробуйте создать свои объекты с методами, чтобы закрепить знания.

 А впереди нас ждет много интересного: поведение this в стрелочных функциях и разных режимах браузера, а также способы работы с контекстом.

Поведение ключевого слова this в JavaScript существенно зависит от режима выполнения кода. Во избежание ошибок, рекомендую принудительно включать «use strict». 

Он решает типичную ошибку: когда this указывает на глобальный объект, а не то, что подразумевал разработчик. Чтобы избежать этого, в «строгом» режиме значение this в функциях, которые вызываются без контекста, будет равно undefined, а не глобальному объекту.

Современные браузеры по умолчанию включают «строгий» режим только при загрузке модулей и в классах.

Жду вас в следующих статьях!

Комментарии (0)