Если вы работали с JavaScript, вы, вероятно, встречали загадочное ключевое слово this. Его поведение может быть несколько сложным для понимания и зависит от контекста, в котором вызывается функция. Итак, что это?

Понимание параметров JavaScript

Начнем с простого примера. Рассмотрим следующую функцию:

function myFn(a, b) {
  console.log(a, b);
}

Когда мы спрашиваем: «Каковы значения a и b?», ответ таков: мы не знаем. Только когда вызывается функция и передаются аргументы, эти параметры принимают определенное значение. Например:

myFn(1, 2); // Outputs: 1, 2

Погружаемся глубже с помощью простой функции

Представьте, что мы создали следующую функцию:

function myFn(a, b) {
  console.log(this, a, b);
}

На первый взгляд, когда нас спрашивают: «Каково значение this?», наш инстинктивный ответ остается в соответствии с нашим предыдущим пониманием: это неоднозначно. Сущность this, сродни a и b, раскрывается только при активном вызове функции.

Контекстные сценарии для этого:

  1. В открытом диком мире (глобальный контекст):

Выполнение myFn(1, 2); выведет: Window (или глобальный объект в среде Node.js), за которым следуют 1 и 2.

myFn(1, 2); // Outputs: Window (or global object in Node.js), 1, 2

2. В области объекта (объектный метод):

Взяв объект myObj, который имеет myFn в качестве одного из своих методов:

var myObj = {myFn, name: 'myObj'};
myObj.myFn(1, 2); // Outputs: { myFn: [Function: myFn], name: 'myObj' }, 1, 2

Будет напечатано: { myFn: [Function: myFn], name: 'myObj' }, за которым следуют 1 и 2.

3. Явная установка this с .call():

Используя метод .call(), мы можем явно указать значение this:

myFn.call('Hello World!', 1, 2); // Outputs: String {"Hello World!"}, 1, 2

Выход? Строковый объект, представляющий "Hello World!", за которым следуют 1 и 2.

Хотя сравнение this с обычным параметром функции может показаться интуитивно понятным, в этом заключается тонкая сложность. В отличие от a и b, которым значения присваиваются через аргументы, this не получает свое значение таким прямым образом. Скорее, его сущность проявляется в зависимости от контекста и способа вызова функции.

Расшифровка this: «Скрытый параметр»

Показательной аналогией для понимания this является визуализация его как скрытого или «скрытого параметра» функции. При вызове функции с использованием .call() представьте, что она незаметно подсовывает начальный параметр, предназначенный исключительно для this.

Контроль this

Одной из мощных возможностей JavaScript является возможность управления значением this. Метод .bind() позволяет создать новую функцию, в которой this установлено определенное значение, независимо от того, как вызывается функция.

const myFnWithFixedThis = myFn.bind('this is always this');
myFnWithFixedThis(1, 2); // Outputs: String {"this is always this"}, 1, 2
var myObj = {myFnWithFixedThis , name: 'myObj'};
myObj.myFnWithFixedThis(1,2); // Outputs: String {"this is always this"}, 1, 2

Нюансы this в сценариях обратного вызова

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

Рассмотрим часто используемый setTimeout:

var obj = {
  name: 'ObjName',
  delayedLog: function() {
    setTimeout(function() {
      console.log(this.name);
    }, 1000);
  }
};

obj.delayedLog();  // What will this output?

Можно было бы ожидать, что он зарегистрирует 'ObjName', но на самом деле он зарегистрирует undefined (или ошибку в строгом режиме). Почему? Поскольку this в обратном вызове setTimeout относится к глобальному объекту Window (или global в Node.js), а не obj.

Точно так же при использовании прослушивателей событий:

var btn = document.querySelector('button');
btn.name = 'MyButton';

btn.addEventListener('click', function() {
  console.log(this.name);
});

Если вы нажмете кнопку, будет записано 'MyButton'. Это связано с тем, что в прослушивателе событий this относится к объекту, вызвавшему событие, который в данном случае является элементом кнопки.

Эти особенности подчеркивают важность контекста. Функции обратного вызова, особенно когда они передаются другим функциям или системам, управляемым событиями, не обязательно сохраняют this контекст своей области видимости. Чтобы бороться с этим, разработчики часто используют .bind(), стрелочные функции или другие механизмы для сохранения желаемого контекста this.

Подведение итогов

В JavaScript this изменяются подобно простому параметру функции, но это нечто большее: его значение определяется контекстом, в котором вызывается функция. Понимая этот контекст и используя такие инструменты, как .call(), .apply() и .bind(), разработчики могут использовать возможности this для написания более гибкого и динамичного кода.

Помните, что каждый раз, когда вы сталкиваетесь с this, найдите минутку, чтобы спросить: «В каком контексте вызывается эта функция?» Ответ поможет вам понять его ценность.