Привет, Хабр! Сегодня мы поговорим о this
, потому что без четкого понимания, как работает this
, ваш код может стать источником путаницы и ошибок.
this
в JS — это ключевое слово, которое ссылается на текущий контекст выполнения. Его значение зависит от того, где и как была вызвана функция, а не от того, где она была определена.
В этой статье мы разберем все способы работы с контекстом выполнения, чтобы вы могли уверенно использовать this
в любом сценарии.
Разные контексты this
Глобальный контекст: поведение this вне функций
Когда вы находитесь на верхнем уровне кода — без функций и объектов — this
ссылается на глобальный объект. В браузерах это window
, а в Node.js — global
.
console.log(this); // В браузере выведет объект window
Здесь this
ссылается на глобальный объект. Если вы используете this
вне любого контекста, вы всегда получите глобальный объект.
Но как это повлияет на функции? Рассмотрим следующий пример:
function showContext() {
console.log(this);
}
showContext(); // Вызовет функцию, выведет window (или global в Node.js)
Согласитесь, такое поведение может удивить, особенно если вы ожидали, что this
будет ссылаться на что-то другое. Если вы хотите, чтобы this
указывал на конкретный объект в глобальном контексте, нужно обернуть его в другой объект:
const myObject = {
name: 'My Object',
showContext: showContext,
};
myObject.showContext(); // выведет myObject
Объектный контекст: как this ссылается на текущий объект в методах
Когда функция вызывается как метод объекта, this
указывает на объект, к которому принадлежит метод. Это, возможно, одна из самых удобных особенностей this
.
const user = {
name: 'Алина',
greet() {
console.log(`Hello, my name is ${this.name}`);
},
};
user.greet(); // Hello, my name is Алина
В этом примере, когда greet()
вызывается, this
ссылается на объект user
, и мы получаем доступ к его свойствам. Но что происходит, если мы передадим метод в другую функцию?
const greetFunc = user.greet;
greetFunc(); // Hello, my name is undefined
Здесь this
больше не указывает на user
, потому что метод был вызван вне контекста объекта.
Функциональный контекст
Когда функция вызывается просто как функция (не как метод объекта), this
снова указывает на глобальный объект (в строгом режиме это будет undefined
).
function showThis() {
console.log(this);
}
showThis(); // window (или undefined в strict mode)
Но если вы вызываете функцию как метод объекта, this
будет ссылаться на объект:
const obj = {
value: 42,
showValue() {
console.log(this.value);
},
};
obj.showValue(); // 42
Стрелочные функции
Стрелочные функции сохраняют this
из окружающего контекста. То есть this
в стрелочной функции будет тем же самым, что и this
в родительской функции, где она была объявлена:
const person = {
name: 'Николай',
greet: function () {
const arrowFunc = () => {
console.log(`Hi, I'm ${this.name}`);
};
arrowFunc();
},
};
person.greet(); // Hi, I'm Николай
Здесь стрелочная функция arrowFunc
сохраняет контекст this
, который указывает на объект person
.
Однако будьте осторожны: если вы попробуете использовать стрелочную функцию в методах объектов, это приведет к тому, что this
не будет ссылаться на объект:
const obj = {
name: 'Ирина',
show: () => {
console.log(this.name);
},
};
obj.show(); // undefined
Здесь this
ссылается на глобальный объект (или undefined
в строгом режиме), и вы не получите ожидаемого результата.
Изменение контекста выполнения
Теперь посмотрим, как управлять контекстом выполнения с помощью методов call
, apply
и bind
. Эти инструменты позволяют настраивать, на что ссылается this
, в зависимости от вашего желания.
Методы call, apply и bind: как и когда их использовать для управления контекстом
Метод call
позволяет вызывать функцию с указанным значением this
и аргументами, переданными отдельно.
const person = {
name: 'Артур',
};
function greet(greeting) {
console.log(`${greeting}, my name is ${this.name}`);
}
greet.call(person, 'Hello'); // Hello, my name is Артур
Используем call
, чтобы указать, что this
в функции greet
ссылается на объект person
. Мы также передаем аргумент 'Hello', который функция использует.
Метод apply
работает аналогично call
, но принимает аргументы в виде массива.
const person = {
name: 'Никита',
};
function greet(greeting, punctuation) {
console.log(`${greeting}, my name is ${this.name}${punctuation}`);
}
greet.apply(person, ['Hi', '!']); // Hi, my name is Никита!
Метод bind
создает новую функцию, которая при вызове будет иметь заданное значение this
, а также может принимать предварительно определенные аргументы. В отличие от call
и apply
, bind
не вызывает функцию немедленно, а возвращает новую функцию.
const boundFunction = func.bind(thisArg, arg1, arg2, ...);
const person = {
name: 'Ирина',
};
function greet(greeting) {
console.log(`${greeting}, my name is ${this.name}`);
}
const greetIrina = greet.bind(person);
greetIrina('Hey'); // Hey, my name is Ирина
Здесь создаем новую функцию greetIrina
, которая всегда будет ссылаться на объект person
.
Классы и контекст: как работать с this в классах
Когда мы работаем с классами в JavaScript, важно помнить, что this
в методах классов указывает на экземпляр класса. Однако, если вы передаете метод класса как коллбэк, this
может потеряться.
Пример:
class Person {
constructor(name) {
this.name = name;
this.greet = this.greet.bind(this); // Привязываем контекст
}
greet() {
console.log(`Hello, my name is ${this.name}`);
}
}
const artem = new Person('Артем');
setTimeout(artem.greet, 1000); // Hello, my name is Артем
Здесь используем bind
в конструкторе, чтобы убедиться, что this
всегда ссылается на экземпляр Person
, даже если метод вызывается вне контекста класса. Если бы мы не использовали bind
, мы бы получили undefined
в this.name
при вызове greet
через setTimeout
.
Прочие фичи
Строгий режим: предотвращение неожиданных значений this
Строгий режим — это способ задать правила, которые помогают избежать ошибок, упрощают диагностику и повышают безопасность кода. В строгом режиме, если функция вызывается без контекста (как обычная функция), this
будет undefined
, а не ссылаться на глобальный объект.
function showThis() {
console.log(this);
}
showThis(); // В браузере выведет window
Пример со строгим режимом:
'use strict';
function showThis() {
console.log(this);
}
showThis(); // undefined
Методы объектов и this
Когда вы определяете методы в объектах, важно помнить, что this
внутри метода всегда будет ссылаться на сам объект, даже если метод передается в другую функцию или контекст. Однако, если вы не используете bind
, вы можете столкнуться с неожиданными результатами.
Пример:
const obj = {
name: 'Ирина',
showName() {
console.log(this.name);
},
};
const show = obj.showName;
show(); // undefined (в строгом режиме) или 'Ирина' (в обычном)
Здесь, когда мы вызываем show
, this
не указывает на obj
, что и приводит к неожиданным результатам.
this в обработчиках событий
Когда вы добавляете обработчик события, this
в нем обычно ссылается на
элемент, на который установлено событие.
Пример:
<button id="myButton">Click me</button>
Однако, если вы используете стрелочную функцию как обработчик, this
будет указывать на родительский контекст, а не на элемент.
Пример со стрелочной функцией:
<button id="myButton">Click me</button>
Неявный и явный this
JavaScript различает неявный и явный this
. Неявный this
— это когда this
определяется автоматически в зависимости от того, как функция была вызвана. Явный this
— это когда вы используете методы call
, apply
или bind
, чтобы явно указать, на что ссылается this
.
Неявный this:
const car = {
brand: 'Ford',
showBrand() {
console.log(this.brand);
},
};
car.showBrand(); // Ford
Явный this:
function showBrand() {
console.log(this.brand);
}
const myCar = { brand: 'Toyota' };
showBrand.call(myCar); // Toyota
Пусть ваш код будет ясным, а this
под контролем. Помните, что this
— штука капризная, но, как любой хороший инструмент, в умелых руках творит чудеса.
Больше про языки программирования эксперты OTUS рассказывают в рамках практических онлайн-курсов. С полным каталогом курсов можно ознакомиться по ссылке.
Комментарии (5)
vasyakolobok77
28.09.2024 09:16+1const show = obj.showName;
show(); // undefined (в строгом режиме) или 'Ирина' (в обычном)
В строгом режиме будет ошибка this is undefined, а в обычном режиме напечатает undefined. Скорее всего, вы ненамеренно ошиблись, но лучше такие вещи проверять.
mavludin
28.09.2024 09:16"Если бы мы не использовали
bind
, мы бы получилиundefined
вthis.name
при вызовеgreet
черезsetTimeout
."Тут можно было бы добавить пример, когда эта проблема решается с помощью стрелочной функции и bind не нужен.
Myclass
Очень подробное разьеснение. Надо будет своим студентам при прохождении языка больше уделить этому внимания. У вас хороший стиль подачи. Не задумывались над написанием книг. Удачи вам.