Если вас смущает ключевое слово this в JavaScript, можете не переживать - оно вначале всех сбивает с толку. Однако это не означает, что без знания this можно дальше продолжать спокойно себе работать. this так часто используется в JavaScript и во всех учебных пособиях, что вам рано или поздно придется его освоить. И как только вы поймете this, вы осознаете, что все намного проще, чем вы думали.
К концу этой статьи вы будете знать, что такое this, для чего оно нужно и как его использовать.
Так что такое this
this - это ключевое слово, значение которого изменяется в зависимости от того, как вызывается функция. Существует шесть различных способов работы с this, в каждом из котором оно имеет разное значение:
- в глобальном контексте
- в конструкторе объекта
- в методе объекта
- в простой функции
- в стрелочной функции
- в слушателе событий
Давайте рассмотрим, как this меняется в каждом из шести контекстов.
this в глобальном контексте
Когда this вызывается вне какой-либо функции в глобальной области видимости, в качестве его значения, по-умолчанию, используется объект window в браузере.
console.log(this) // => window
Обычно this редко используют в глобальном контексте, поэтому значение this для данного случая не имеет особого значения.
this в конструкторе объекта
Когда вы создаете новый экземпляр объекта с ключевым словом new, this ссылается на экземпляр.
function Friend(name) {
this.name = name
}
let ivan = new Friend('Иван')
let vasya = new Friend('Вася')
console.log(ivan) // {name: 'Иван'}
console.log(vasya) // {name: 'Вася'}
Как видим, ivan является экземпляром Friend в приведенном выше коде. И теперь всякий раз, когда вы ссылаетесь на ivan, вы не получите случайным образом vasya. Устанавливать this, ссылающийся на экземпляр, имеет смысл.
this в методе объекта
Метод - это модный термин для функции, связанной с объектом, например:
let object = {
aMethod() { ... } // это метод
}
this в любом методе ссылается на сам объект:
let object = {
aMethod() {
console.log(this)
}
}
object.aMethod() // => object
Поскольку this относится к объекту, вы можете использовать методы для получения экземпляра объекта, например:
function Friend(name) {
return {
name,
getName(){
return this.name
}
}
}
const ivan = new Friend('Иван')
const vasya = new Friemd('Вася')
console.log(ivan.getName()) // => 'Иван'
В вышеприведенных двух объектных контекстах вы можете заметить, что изменение значения this позволяет получить корректный экземпляр, что является основой объектно-ориентированного программирования.
this в простой функции
Простые функции - это функции, которые вы наверняка очень хорошо знаете (как показано ниже в примере). Анонимные функции, написанные в той же форме, также считаются простыми функциями.
function hello() {
...
}
В браузерах this в простой функции всегда ссылается на window. Это же утверждение верно, если вы вызываете простую функцию в методе объекта.
function simpleFunction() {
console.log(this)
}
const object = {
method() {
simpleFunction()
}
}
simpleFunction() // => window
object.method() // => window
К сожалению, изменение this неожиданно для новичков. Ведь действительно, на первый взгляд кажется, что this в функции, вызываемой в методе method() должно быть неизменным и ссылаться на объект.
Чтобы понять, почему так происходит, рассмотрим следующий код. Здесь, функция this.hello выполняется позже в функции setTimeout.
const object = {
doSomethingLater() {
setTimeout(function() {
this.hello() // Ошибка
}, 1000)
},
hello() {
console.log('Привет Мир!')
}
}
Приведенный выше код приведет к ошибке. Ошибка возникает из-за того, что в функции setTimeout значение this равно window, а в window нет метода hello.
Один из быстрых фиксов - создать переменную, в которой хранится ссылка на this. Такую переменную часто именуют self или that.
const object = {
doSomethingLater() {
const self = this;
setTimeout(function() {
self.hello() // => 'Привет Мир!'
}, 1000)
},
hello() {
console.log('Привет Мир!')
}
}
Второй способ решить эту проблему - использовать новые стрелочные функции ES6, о которых написано ниже.
this в стрелочной функции
this в стрелочной функции всегда такое же, ка this вокруг нее (т.е. в ее непосредственной области видимости). Таким образом, если вы используете стрелочные функции в методе объекта, контекст this остается объектом, а не window.
С помощью стрелочных функций пример выше можно было бы записать следующим образом:
const object = {
doSomethingLater() {
setTimeout(() => this.hello(), 1000)
},
hello() {
console.log('Привет Мир!')
}
}
Третий способ изменить значение this в любой функции - использовать либо bind, либо call, либо apply. Рассмотрим bind чуть позже.
this в слушателе событий
В слушателе событий this ссылается на элемент, который инициировал событие:
let button = document.querySelector('button')
button.addEventListener('click', function() {
console.log(this) // button
})
При создании более сложных компонентов вы можете создать слушатели событий внутри методов:
function Foo(elem) {
return {
listenClick() {
elem.addEventListener('click', function () {
...
})
}
}
}
Поскольку this ссылается к элементу в слушателе событий, то если вам нужно активировать другой метод в этом слушателе, для него необходимо создать ссылку на объект:
function Foo(elem) {
return {
listenClick() {
const self = this;
elem.addEventListener('click', function () {
self.hello()
})
},
hello() { console.log('Привет Мир!') }
}
}
В качестве альтернативы вы можете использовать стрелочную функцию. В этом случае вы все равно сможете получить элемент с помощью event.currentTarget.
function Foo(elem) {
return {
listenClick() {
elem.addEventListener('click', (e) => {
console.log(e.currentTarget) // элемент, на котором произошло событие
this.hello()
})
},
hello() { console.log('Привет Мир!') }
}
}
Однако оба способа недостаточно хороши, если вам, к примеру, нужно будет удалить слушатель события, поскольку функции анонимны.
Чтобы удалить слушатель события, обратный вызов, переданный в качестве второго параметра, должен быть именованной функцией:
function helloFunction() {
console.log('Привет Мир!')
// Удаляем слушатель
document.removeEventListener('click', helloFunction)
}
document.addEventListener('click', helloFunction)
Если же вам нужен this в качестве ссылки на объект в слушателе событий, вам нужно использовать bind, чтобы уже вручную создать контекст this.
function Foo(elem) {
return {
listenClick() {
this.listener = this.hello.bind(this)
elem.addEventListener('click', this.listener)
},
hello(e) {
const elem = e.currentTarget;
console.log('Привет Мир!');
elem.removeEventListener('click', this.listener)
}
}
}
Приведенный выше код может запутать вас, если вы не понимаете bind. Давайте разберемся, что он делает.
Изменение this с помощью bind
bind - это метод, имеющийся в каждой функции. Он позволяет нам изменять контекст this. Этот метод принимает любое количество аргументов и возвращает связанную функцию.
function showThis() {
console.log(this)
}
const boundFunc = showThis.bind( ... )
Первый параметр, который вы передаете в bind, становится this в связанной функции. Создав такую функцию, вы можете вызывать ее в любое время:
function showThis() {
console.log(this)
}
const boundFunc = showThis.bind({name: 'Иван'});
boundFunc() // => {name: 'Иван'}
Другие параметры, которые вы передаете в bind, будут переданы в качестве аргументов исходной функции.
function showParams(...args) {
console.log(...args)
}
const boundFunc = showParams.bind(null, 1, 2, 3);
boundFunc() // => 1, 2, 3
Примечание - bind не работает со стрелочными функциями.
Теперь давайте вернемся к коду с слушателями событий выше и проанализируем, что происходит:
function Foo(elem) {
return {
listenClick() {
// связываем this.hello с ссылкой на экземпляр
// устанавливаем привязанную функцию к this.listener, чтобы удалить ее позже
this.listener = this.hello.bind(this)
// добавляем слушатель события
elem.addEventListener('click', this.listener)
},
hello(e) {
// получаем элемент, чтобы удалить слушатель события
const elem = e.currentTarget;
// выполняем тело функции
console.log('Привет Мир!');
// удаляем слушатель события
elem.removeEventListener('click', this.listener)
}
}
}
Комментарии (0)