Полный цикл в digital

Ключевое слово this

Поведение ключевого слова this зависит от контекста, в котором оно используется, и от того, в каком режиме оно используется — строгом или нестрогом.

Существует четыре основных контекста, в которых можно неявно определить значение ключевого слова this:

  1. Глобальный контекст
  2. Как метод внутри объекта
  3. Как конструктор в функции или классе
  4. Как обработчик событий DOM

Глобальный контекст

В глобальном контексте this ссылается на глобальный объект. Когда вы работаете в браузере, глобальный контекст — это окно. Когда вы работаете в Node.js, глобальный контекст — это global.

Если вы зарегистрируете значение this без какого-либо другого кода:

console.log(this)

Вы увидите к какому объекту относится this:

Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}

Функция верхнего уровня сохранит ссылку this на глобальный объект. Для примера давайте напишем функцию верхнего уровня или функцию, которая не связана ни с одним объектом:

function printThis() {
    console.log(this)
}
printThis()

Результат работы:

Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}

Даже внутри функции this все равно относится к window, или к глобальному объекту.

Однако при использовании строгого режима контекст this внутри функции в глобальном контексте будет undefined:

'use strict'
function printThis() {
    console.log(this)
}
printThis()

Результат работы:

undefined

Как правило, строгий режим использовать безопаснее, так как он позволяет уменьшить вероятность непредвиденной области применения ключевого слова this. Вряд ли кто-то захочет обратиться к объекту window, используя this.

Метод объекта

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

const america = {
    name: 'The United States of America',
    yearFounded: 1776,
    describe() {
        console.log(`${this.name} was founded in ${this.yearFounded}.`)
    },
}
america.describe()

Результат работы:

"The United States of America was founded in 1776."

В этом примере this, будет указывать america.

Во вложенном объекте this ссылается на текущую область метода. В следующем примере this.symbol в объекте details ссылается на details.symbolcode>:

const america = {
    name: 'The United States of America',
    yearFounded: 1776,
    details: {
        symbol: 'eagle',
        currency: 'USD',
        printDetails() {
            console.log(`The symbol is the ${this.symbol} and the currency is ${this.currency}.`)
        },
    },
}
america.details.printDetails()

Результат работы:

"The symbol is the eagle and the currency is USD."

Проще говоря, this ссылается на объект с левой стороны от точки при вызове метода.

Конструктор функций

Когда вы используете ключевое слово new, оно создает экземпляр функции или класса конструктора. Конструкторы функций были стандартным способом инициализации пользовательского объекта до того, как в 2015 вместе с обновлением ECMAScript для JavaScript появился синтаксис класса.

function Country(name, yearFounded) {
    this.name = name
    this.yearFounded = yearFounded
    this.describe = function() {
        console.log(`${this.name} was founded in ${this.yearFounded}.`)
    }
}
const america = new Country('The United States of America', 1776)
america.describe()

Результат работы:

"The United States of America was founded in 1776."

В этом контексте this ссылается на экземпляр Country, который содержится в константе america.

Конструктор класса

Конструктор в классе действует так же, как в функции:

class Country {
    constructor(name, yearFounded) {
        this.name = name
        this.yearFounded = yearFounded
    }
    describe() {
        console.log(`${this.name} was founded in ${this.yearFounded}.`)
    }
}
const america = new Country('The United States of America', 1776)
america.describe()

Ключевое слово this в методе describe относится к экземпляру Country, которым является america.

Результат работы:

"The United States of America was founded in 1776."

Обработчик событий DOM

В браузере есть специальный контекст this для обработчиков событий. В обработчике событий, вызываемом addEventListener ключевое слово this будет ссылаться на event.currentTarget. Чаще всего по мере необходимости разработчики просто используют event.target или event.currentTarget для доступа к элементам в DOM, но так как ссылка this изменяется в этом контексте, это важно знать.

В следующем примере мы создадим кнопку, добавим к ней текст и поместим ее в DOM. Когда мы записываем значение this в обработчик события, он выводит цель.

const button = document.createElement('button')
button.textContent = 'Click me'
document.body.append(button)
button.addEventListener('click', function(event) {
    console.log(this)
})

Как только вы вставите этот код в свой браузер, на странице появится кнопка с надписью Click me. Если вы нажмете кнопку, вы увидите Click me в консоли, так как нажатие кнопки регистрирует элемент, который является самой кнопкой. Как видите, this ссылается на целевой элемент, который является элементом, к которому мы добавили прослушиватель событий.

Явный контекст

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

Разница между методами call, apply и bind заключается в следующем:

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

Таким образом, call и apply обеспечивает немедленное выполнение функции с различными форматами аргументов, а bind создаёт многоразовые функции с фиксированными контекстами

Методы call и apply

Методы call и apply очень похожи, они вызывают функцию с указанным контекстом this и дополнительными аргументами. Единственная разница между call и apply заключается в том, что call требует, чтобы аргументы передавались по одному, а apply принимает их в виде массива.

В этом примере мы создадим объект и функцию, которая ссылается на this, но не имеет контекста this:

const book = {
    title: 'Brave New World',
    author: 'Aldous Huxley',
}
function summary() {
    console.log(`${this.title} was written by ${this.author}.`)
}
summary()

Результат работы:

undefined was written by undefined

Поскольку summary и book не связаны, сам по себе вызов summary будет выводить только неопределенное значение undefined, так как он ищет эти свойства в глобальном объекте. Попытка сделать это в строгом режиме приведет к Uncaught TypeError: Cannot read property ‘title’ of undefined, так как само ключевое слово this будет undefined.

Тем не менее, вы можете использовать call и apply для вызова контекста this для book в функции:

summary.call(book)

Или:

summary.apply(book)

Результат работы:

Brave New World was written by Aldous Huxley.

Теперь между book и summary существует связь. Давайте точно узнаем, на что ссылается this:

const book = {
    title: 'Brave New World',
    author: 'Aldous Huxley',
}

function printThis() {
    console.log(this)
}

printThis.call(book)
// или
printThis.apply(book)

Результат работы:

{title: "Brave New World", author: "Aldous Huxley"}

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

Как мы уже говорили, call и apply почти одинаковы, но есть одно небольшое отличие. Помимо возможности передавать контекст this в качестве первого аргумента, вы также можете передавать apply дополнительные аргументы:

function longerSummary(genre, year) {
    console.log(
        `${this.title} was written by ${this.author}. It is a ${genre} novel written in ${year}.`
    )
}

При использовании call каждое дополнительное значение, которое вы хотите передать, отправляется в качестве дополнительного аргумента:

longerSummary.call(book, 'dystopian', 1932)

Результат работы:

Brave New World was written by Aldous Huxley. It is a dystopian novel written in 1932.

Если вы попытаетесь отправить те же аргументы с помощью apply, произойдет ошибка. При использовании apply вы должны передать все аргументы в массиве:

longerSummary.apply(book, ['dystopian', 1932])

Результат работы:

Brave New World was written by Aldous Huxley. It is a dystopian novel written in 1932.

Метод bind

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

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

const book = {
    title: 'Brave New World',
    author: 'Aldous Huxley',
}
function summary() {
    console.log(`${this.title} was written by ${this.author}.`)
}

const braveNewWorldSummary = summary.bind(book)

braveNewWorldSummary()

Результат работы:

Brave New World was written by Aldous Huxley

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

const book = {
    title: 'Brave New World',
    author: 'Aldous Huxley',
}
function summary() {
    console.log(`${this.title} was written by ${this.author}.`)
}
const braveNewWorldSummary = summary.bind(book)
// Brave New World was written by Aldous Huxley.
braveNewWorldSummary()
const book2 = {
    title: '1984',
    author: 'George Orwell',
}
braveNewWorldSummary.bind(book2)
// Brave New World was written by Aldous Huxley.
braveNewWorldSummary()

this и стрелочные функции

Стрелочные функции не имеют привязки this. Вместо этого они переходят на следующий уровень исполнения.

const whoAmI = {
    name: 'Leslie Knope',
    regularFunction: function() {
        console.log(this.name)
    },
    arrowFunction: () => {
        console.log(this.name)
    },
}
// "Leslie Knope"
whoAmI.regularFunction()
// undefined
whoAmI.arrowFunction()

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

В этом примере мы создадим и добавим кнопку в DOM, как мы делали раньше, но у класса будет прослушиватель событий, который будет изменять текстовое значение кнопки при нажатии:

const button = document.createElement('button')
button.textContent = 'Click me'
document.body.append(button)
class Display {
    constructor() {
        this.buttonText = 'New text'
        button.addEventListener('click', event => {
            event.target.textContent = this.buttonText
        })
    }
}
new Display()

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

Заполните форму уже сегодня!
Для начала сотрудничества необходимо заполнить заявку или заказать обратный звонок. В ответ получите коммерческое предложение, которое будет содержать индивидуальную стратегию с учетом требований и поставленных задач
Работаем по будням с 9:00 до 18:00. Заявки, отправленные в выходные, обрабатываем в первый рабочий день до 12:00.
Спасибо, ваш запрос принят и будет обработан!
Эйч Маркетинг