Глобальное это

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

this.name = 'Kudzanayi';
console.log(window.name); // Kudzanayi 
console.log(name); // Kudzanayi 
console.log(this.name); // Kudzanayi

это внутри функции, вызываемой на объекте

Предположим, у нас есть следующий объект:

const kudzanayi = {
   name: 'Kudzanayi',
   sayHello() {
       console.log(`Hello, I'm ${this.name}`
      }
   }

Если мы вызовем функцию sayHello для объекта kudzanayi следующим образом:

joe.sayHello(); //prints 'Hello, I'm Joe' 

Тогда this внутри функции sayHello ссылается на объект kudzanayi.

Когда вы вызываете функцию для объекта, используя запись через точку, как мы делали выше, вы можете сказать, что this относится к объекту перед точкой. Иногда его называют приемником.

Однако если мы сохраним ссылку на функцию sayHello и вызовем ее через ссылку, мы получим другой результат:

const greet = joe.sayHello;
greet() //prints "Hello, I'm undefined"

Когда в вызове функции нет явного получателя, this относится к глобальному объекту. Если ничто другое не установило свойство name для оконного объекта, это напечатает Hello, I'm undefined

Если какой-то другой код установил свойство имени для объекта окна, он напечатает его вместо этого. Рассмотрим следующий код:

name = 'Rutendo'
const kudzanayi = {
   name: ' Kudzanayi',
   sayHello() {
      console.log(`Hello, I'm ${this.name})
     }
  }
kudzanayi.sayHello();// prints "Hello, I'm kudzanayi"
const greet = kudzanayi.sayHello
greet()// "Hello, I'm Rutendo"
const farai = {
  name: 'Farai'
  sayHello: kudzanayi.sayHello
 }
farai.sayHello();// 'Hello, I'm Farai'

this внутри прослушивателя событий

Другим распространенным сценарием является прослушиватель событий. При добавлении прослушивателя событий указывается функция обратного вызова для обработки события. Когда вызывается этот обратный вызов, это относится к объекту, к которому был добавлен прослушиватель событий.

document.querySelector('button.myButton').addEventListener('click', function(){ this.style.background = 'red'})

Здесь мы добавили прослушиватель кликов к кнопке. Когда кнопка нажата и функция обратного вызова выполняется, this относится к кнопке.

это внутри обратного вызова

В Array.prototype есть несколько полезных функций, таких как forEach, map, reduce Каждая из них принимает функцию обратного вызова в качестве аргумента.

Внутри обратного вызова, переданного этим функциям, this снова ссылается на глобальный объект.

const arr = [1,2,3]
arr.forEach(function(item){ 
 console.log(this)
})

Когда приведенный выше код запускается в браузере, он трижды выводит объект window на консоль.

Рассмотрим следующий код

name = 'rutendo'
const kudzanayi = {
   name: 'kudzanayi'
   greet(people){
     people.forEach(function(person){
        console.log(`Hello ${person}, I'm ${this.name}`)
      })
    }
}
kudzanayi.greet(['farai', 'oswald'])

Следующее будет производить

Hello farai, I'm rutendo
Hello oswald, I'm rutendo

Несмотря на то, что функция greet имеет значение this объекта kudzanayi, внутри обратного вызова к forEach значением this.name является Боб, который был установлен для объекта window.

Как нам изменить код, чтобы он печатал кудзанайи вместо рутэндо

Один из способов — изменить ссылку на this и указать ее внутри обратного вызова:

name = 'rutendo'
const kudzanayi = {
   name: 'kudzanayi'
   greet(people){
    const self = this
    people.forEach(function(person){
        console.log(`Hello ${person}, I'm ${this.name}`)
      })
    }
}
kudzanayi.greet(['farai', 'oswald'])

Получаем желаемый результат

Hello farai, I'm kudzanayi
Hello oswald, I'm kudzanayi

Почему умирает эта работа? Поскольку функция наследует окружающую область (спасибо, закрытие), значение self можно получить из функции обратного вызова.

Но есть лучшие способы сделать это

Изменение значения этого

используя функцию стрелки

Самый простой способ выполнить то, что делает предыдущий пример кода, — использовать функцию стрелки вместо синтаксиса function() {...}.

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

name = 'rutendo'
const kudzanayi = {
   name: 'kudzanayi'
   greet(people){
       people.forEach(person => console.log(`Hello ${person}, I'm ${this.name}`))
    }
}
kudzanayi.greet(['farai', 'oswald'])

вывод такой же как и раньше

Hello farai, I'm kudzanayi
Hello oswald, I'm kudzanayi

Значение this внутри обратного вызова стрелки — это объект kudzanayi.

Используйте Function.prototype.bind

В прототипе Function. есть несколько удобных функций. Одна из них — bind. С помощью этой функции вы можете изменить то, на что ссылается this в функции.

const kudzanayi = {
     name: 'kudzanayi'
    sayHello(){
         console.log(`Hello, I'm ${this.name}`)
       }
    }
const greet = kudzanayi.sayHello;
 greet();

Мы уже знаем, что код напечатает undefined вместо kudzanayi. Однако мы можем исправить это, вызвав bind

const kudzanayi = {
     name: 'kudzanayi'
    sayHello(){
         console.log(`Hello, I'm ${this.name}`)
       }
    }
const greet = kudzanayi.sayHello.bind(kudzanayi)
greet() // prints 'Hello, I'm Kudzanayi'

Вот что делает bind: Вызов bind для функции, как мы сделали выше, возвращает новую функцию, значение this которой привязано к первому аргументу, переданному в bind.

kudzanayi.sayHello — это ссылка на функцию sayHello. Мы вызываем bind(kudzanayi) для этой функции, которая возвращает новую функцию, в которой this привязан к объекту joe. Так что код работает как задумано.

Используйте Function.prototype.call или function.prototype, примените

Две другие полезные функции прототипа Function — это call и apply. У них обоих один и тот же результат, просто они подходят к нему немного по-разному, как мы сейчас увидим.

const kudzanayi = {
     name: 'kudzanayi'
     greet(person){
        console.log(`Hello ${person}, I'm ${this.name}`)
      }
  }
const greet = kudzanayi.greet;
greet('rutendo'); // Hello rutendo, I'm undefined
greet.call(kudzanayi, 'rutendo) // Hello rutendo, I'm kudzanyi
greet.apply(kudzanayi, ['rutendo'])// Hello rutendo, I'm kudzanayi

call и apply оба выполняют одни и те же цели, но есть небольшая разница в том, как они используются.

Оба они вызывают функцию с первым аргументом, привязанным к значению this.

Это похоже на bind, как мы делали выше, но с одним ключевым отличием. Bind возвращает новую функцию, которая всегда будет иметь указанное значение this для вызова. call и apply работают с исходной функцией, и их эффекты применяются только к этому единственному вызову.

Разница между ними заключается в том, как мы указываем аргументы для вызовов функций. call принимает переменное количество аргументов, а apply принимает аргументы в виде массива.