Ресурсы: Большой гайд по this
Контекст вызова this - это то, что окружает функцию и в каких условиях она вызывается.
Функция может вызываться четырьмя способами и в каждом из них контекст вызова отличается.
1. Обычный вызов функции Код
function showThis() {
console.log(this);
}
showThis();
В данном случае
this будет вести себя по-разному, в зависимости от того, установлен ли строгий режим или нет.
Если строгий режим не установлен, то в консоли мы получим глобальный объект
Window. Таким образом, функция, запускаемая обычным образом, в отсутствии строго режима имеет контекст, который будет ссылаться на глобальный объект
Window Если мы подключаем строгий режим, то
this примет значение
undefined Разбираем на примере, внутри нашей функции добавим еще одну функцию, которая должна возвращать сумму двух аргументов
Код
'use strict';
function showThis(a, b) {
console.log(this); // undefined
function sum() {
console.log(this); // undefined
return this.a + this.b;
}
console.log(sum());
}
showThis(4, 5);
Вопрос: какой контекст вызова будет у функции
sum и будет ли работать
return?
В данном случае у функции
sum контекст вызова будет равен
undefined, поскольку мы вызываем её в строгом режиме.
Так как контекст вызова равен
undefined, вот этот код не сработает:
Код
return this.a + this.b;
А чтобы он сработал, используем замыкание функции и просто пишем:
Функция
sum ищет и не находит эти переменные внутри себя и потом поднимается на уровень выше, где их и обнаружит.
2. Вызов функции как метода объекта Код
'use strict';
const obj = {
a: 20,
b: 15,
sum: function() {
console.log(this);
}
}
obj.sum();
В консоли мы получим тот объект, в котором находится этот метод.
Цитата
Если мы используем метод внутри объекта, тот контекст вызова будет ссылаться на этот объект.
Если внутри метода объекта будет еще вызываться какая-то функция:
Код
'use strict';
const obj = {
a: 20,
b: 15,
sum: function() {
function shout() {
console.log(this);
}
shout();
}
}
obj.sum();
То в консоли мы получим
undefined, потому что это уже обычный вызов функции, как в примере выше. А так как у нас включен строгий режим, то при таком вызове функции контекст будет
undefined.
3. Вызов функции-конструктора через оператор new Код
function User(name, id) {
this.name = name;
this.id = id;
this.human = true;
}
const ivan = new User('Ivan', 28); // создаётся новый объект, на него ссылается this
Внутри функции-конструктора и внутри класса контекст вызова для всех методов и свойств будет только что созданный новый объект.
То есть, в примере выше
this ссылается на объект
ivan. Если мы сейчас выведем в консоль
this, то убедимся в этом:
Код
function User(name, id) {
this.name = name;
this.id = id;
this.human = true;
console.log(this);
}
const ivan = new User('Ivan', 28);
Цитата
User { name: 'Ivan', id: 28, human: true }
4. Ручное присваивание this любой функции Разбираем на примере, создадим какую-то функцию:
Код
function sayName() {
console.log(this);
console.log(this.name);
}
Помимо этого в переменной
user мы запишем объект, у которого будет свойство
name Код
const user = {
name: 'John';
}
Как теперь сделать так, чтобы функция при её запуске могла получить доступ к свойству
name объекта
user?
Для этого есть два метода
call() и
apply() Код
sayName.call(user);
sayName.apply(user);
И теперь в консоли мы увидим в качестве контекста вызова наш объект
user, к которому мы привязали нашу функцию, а также увидим значение свойства
name привязанного объекта
Цитата
{ name: 'John' }
John
{ name: 'John' }
John
Причём, как видим, результат при использовании обоих методов, что при
call, что при
apply, одинаков.
Функция, благодаря использованию этих методов, приобретает свой контекст в виде объекта
user.
Два этих метода выполняют одно и тоже, разница у них будет, когда функция будет принимать какие-то дополнительные аргументы.
При использовании
call они просто передаются чере запятую, при использовании
apply, они передаются также через запятую, но внутри массива.
Код
function sayName(surname) {
console.log(this);
console.log(this.name + surname);
}
const user = {
name: 'John';
}
sayName.call(user, 'Smith');
sayName.apply(user, ['Smith']);
Есть еще один метод ручного присваивания контекста, и этот метод
bind(), он создаёт новую функцию, связанную с определённым контекстом.
Код
function count(num) {
return this*num;
}
Здесь нам не хватает какого-то контекста вызова, поэтому мы создаём новую переменную и помещаем в неё
новую функцию Код
const double = count.bind(2); // здесь будет новая функция!
И теперь двойка у нас в качестве
this помещается в функцию
count.
Пробуем вызвать функцию
double:
Код
console.log(double(3)); // 6
console.log(double(4)); // 8
Контекст событий и обработчики событий
На странице есть кнопка, получаем её и добавляем ей обработчик события
Код
const btn = document.querySelector('button');
btn.addEventListener('click', function() {
console.log(this);
});
При работе данного кода в консоли мы будем получать саму же кнопку.
Если в обработчике события коллбек-функция записана в классическом виде, то контекстом вызова будет сам элемент, на котором произошло событие.
По-простому, в таком случае
this будет тоже самое, что и
event.target и мы можем в этом убедиться
Код
btn.addEventListener('click', function() {
this.style.backgroundColor = 'red'; // кнопка при клике станет красной
});
Стрелочные функции и контекст вызова
У стрелочных функций
нет своего контекста вызова и его они берут всегда у своего родителя.
Код
const obj = {
num: 5,
sayNumber: function() {
const say = () => {
console.log(this);
};
say();
}
}
obj.sayNumber();
Если бы в этом примере
say была бы в классическом стиле прописана, то мы получили бы в консоль
undefined, но так как она прописана стрелочной функцией, то свой контекст она берет у своего родителя, в данном случае это метод
sayNumber, у него же, в свою очередь, контекст ссылается на объект, в котором этот метод содержится.
Соответственно, в коде выше
this будет ссылаться на сам объект, убеждаемся в этом, получив в консоли:
Цитата
{ num: 5, sayNumber: [Function: sayNumber] }
Особенности стрелочной функции:
классическая запись:
Код
const double = function(a) {
return a * 2;
}
если используется один аргумент, можно обойтись без круглых скобок, а если тело функции умещается в одну строку, то и без фигурных тоже, при этом опускается слово
return Код
const double = a => a * 2;
console.log(double(4)); // 8
Если же больше одного аргумента, то понадобятся круглые скобки:
Код
const double = (a, b) => 2 * a * b;
Если же в обработчике событий будет стрелочная функция:
Код
btn.addEventListener('click', () => {
this.style.backgroundColor = 'red';
});
тогда контекст вызова будет
undefined, он теряется, потому что у стрелочной функции своего контекста вызова нет и она обращается к
undefined, а при отсутствии строгого режима к
window. Поэтому, чтобы добиться того же результата, что и в случае классического оформления функции, мы используем объект события:
Код
btn.addEventListener('click', (e) => {
e.target.style.backgroundColor = 'red';
});
И теперь при клике кнопка также покрасится в красный цвет.
Добавлять комментарии могут только зарегистрированные пользователи.