this в JavaScript — что такое контекст
Одна из причин, по которой ключевое слово this
такое не простое для многих программистов JavaScript — даже опытных — заключается в том, что к чему относится this
, полностью зависит от контекста, иногда довольно сложным образом. Так, чтобы разобраться с this
, важно не только понять, как «this» ведет себя в различных контекстах, но и понять сами контексты — и как их определить в живом коде.
Читайте также:
- Методы объекта, «this»
- .bind() — Привязка контекста к функции
- .call() и .apply()
Оглавление:
Теоретические основы
this
— это ключевое слово в JavaScript которое содержит в себе объект (контекст) выполняемого кода.
Мне кажется что проще всего представить что this
— это уникальная переменная, которая хранит в себе контекст исполняемого кода. И наоборот — контекст — это значение ключевого слова this.
this
имеет различные значения в зависимости от того, где используется:
- Сама по себе this относится к глобальному объекту (window).
- В методе this относится к родительскому объекту.
- В функции this относится к глобальному объекту.
- В функции в ‘strict mode’ this = undefined.
- В стрелочной функции this относится к контексту где функция была создана.
- В событии this ссылается на элемент запустивший событие.
Что такое контекст?
Любое определение this, так или иначе крутится вокруг слова контекст. Давайте разберемся что же это такое.
JavaScript является однопоточным языком, то есть одновременно может выполняться только одна задача. Интерпретатор JavaScript всегда начинает выполнять код с глобального контекста (в браузере это объект window). С этого момента у самой первой вызванной функции контекстом будет глобальный объект (window). Наша функция может создать новый объект, создать в нем методы и запустить один из этих методов, теперь контекст вызова изменился и стал указывать на новый объект, который был создан из глобального контекста.
Таким образом, контекстом всегда является какой-то объект из под которого был вызван метод (функция).
По ходу обработки кода внутри глобального контекста window создаются другие объекты, они представляют собой новые контексты в которых выполняется код. Таким образом, в одном коде может быть замешено много контекстов. Одна функция может вызывать методы разных объектов и у каждого из них будет свой контекст.
Контекст
— это всегда значение ключевого слова this, которое является ссылкой на объект, который запустил метод (функцию). Контекст — это объект «владеющий» исполняемым кодом. А this
всегда ссылаться на объект (контекст) запустивший функцию.
Методы и функции
В теле каждого метода или функции, которая по сути всегда является методом какого-то объекта мы можем использовать this
, чтобы обратится к родительскому объекту который вызвал функцию или в котором находится метод. Упрощенно можно сказать — this
— это родительский объект который вызвал метод (функцию).
this
используется внутри функции my_function, можно сказать что this
это универсальная переменная в значении которой лежит объект, который вызвал функцию my_function.Это удобно, потому что:
- Неудобно каждый раз писать в коде название родительского объекта, чтобы вызвать его метод (функцию), а можно написать
this.my_function()
вместоwindow.my_object.my_function()
. - Иногда мы вообще можем заранее не знать объект который вызвал функцию. Потому что программист написал код так, что объект (контекст) нужно указать при вызове функции (этот подход позволяет использовать одну и ту же функцию/метод, для разных объектов).
Представить что this
это переменная ссылающаяся на объект, который вызвал метод (функцию) сильно упрощает понимание и использование this в вашем коде.
Важно понять, что this не зависит от того где была объявлена функция, а зависит от того, как (кем) функция была вызвана
Область видимости (scope) и Контекст
Кроме контекста нужно еще понимать что такое область видимости. Эти понятия часто путают. Называя одно другим. Однако контекст и область видимости это разные вещи.
Каждый вызов функции имеет как область видимости, так и контекст, связанный с ней. Область видимости основана на том где функция вызывается (какие переменные ей доступны), а контекст основан на том, кем функция вызывается (каким объектом). Другими словами, область видимости относится к доступу функции к переменным при ее вызове и является уникальной для каждого вызова. Контекст — это всегда значение ключевого слова this, которое является ссылкой на объект, «владеющий» текущим исполняемым кодом.
Подробнее Scope and Context in JavaScript.
this по умолчанию
Чаще всего this используется внутри функции/метода. Однако this также работает в глобальной области.
Когда мы пишем простую функцию и затем её используем, она все равно вызывается каким-то объектом и this ссылается на этот вызывающий объект. Для браузера — это объект window.
Запустим такой код в консоли браузера:
function get_this(){ return this } get_this() // ▸ Window {0: Window, window: Window, self: Window, …} this // ▸ Window {0: Window, window: Window, self: Window, …}
Как мы видим в обоих случаях this будет объектом Window. Это равносильно такому коду:
window.get_this = function(){ return this } window.get_this() // ▸ Window {0: Window, window: Window, self: Window, …} window.this // ▸ Window {0: Window, window: Window, self: Window, …}
Однако если функция выполняется в строгом режиме, то в this будет записано undefined, так как в этом режиме запрещены привязки по умолчанию:
function get_this(){ 'use strict' return this } get_this() // ▸ undefined window.get_this() // ▸ Window {0: Window, window: Window, self: Window, …}
Т.е. в строгом режиме нужно прямо указывать контекст из которого вызывается метод:
this в методах
Метод вызывается наглядно: сначала идет название объекта ourObject
, затем точка .
, затем название метода ourMethod
.
this очень легко отследить, когда метод вызывается в сочетании с объектом
— что находится с левой стороны от точки, то и будет в this вызываемого метода (в данном случае this = object):
let object = { method: function(){ console.log( this ) } } object.method() // ▸ {method: ƒ}
Это самый простой вариант чтобы понять что будет находится в this. Тут метод вызван из того же объекта в котором он определен. Поэтому this ссылается на экземпляр object потому что method вызывается этим объектом. this тут это вызывающий объект.
this в функции-конструктора
Когда для создания новых объектов используется функция, её нужно вызывать с помощью ключевого слова new. При таком вызове функция будет вызвана как конструктор нового объекта и новый объект будет возвращен.
Название функции в этом случае принято писать с заглавной буквы. Это название станет названием экземпляра объекта.
Когда вызывается конструктор, this указывает на вновь созданный объект, а не на объект который запустил построение.
function Doge( param ) { // this = {} - новый пустой объект, который вызвал эту функцию благодаря new this.saying = param // return this // это конструктор, он не может ничего возвращать } new Doge( 'Привет' ) // ▸ Doge {saying: "Привет"} new Doge( 'Браузер' ) // ▸ Doge {saying: "Браузер"}
Заглянем под капот, чтобы лучше понять почему this в функции-конструктора работает именно так. Когда функция вызывается через new
вызов этой функции происходит иначе:
- Создается пустой объект, название которого берется из названия функции.
- Созданный объект вызывает функцию как конструктор. Так как функцию вызывает новый объект, то this внутри этой функции ссылается на этот новый объект.
- Функция-конструктор отрабатывает и
new
возвращает вновь созданный объект.
В функции не нужно указывать
Усложним: вызов функции внутри конструктора
Что будет если мы поместим обычную функцию внутрь функции-конструктора и вызовем её там?
У нас есть глобальная функция getThis(), функция-конструктора и новый объект, созданный функцией-конструктора. В этом примере глобальная getthis() вызываются изнутри функции-конструктора:
function get_this(){ console.log( this ) } function Doge( saying ){ this.saying = saying get_this() } new Doge( 'Не Window!' ) // увидим в консоли: // ▸ Window {0: Window, window: Window, self: Window, …} // ▸ Doge {saying: "Не Window!"}
get_this() все еще указывает на Window, потому что, несмотря на то что get_this() вызывается внутри функции конструктора Doge(), она фактически вызывается из глобальной области — window.get_this()
.
А что, если мы создадим метод внутри конструктора и вызовем его в конструкторе при создании нового объекта?
function Doge( saying ){ this. saying = saying this.get_this = function(){ console.log( this ) } this.get_this() } new Doge( 'не Window' ) // создадим экземпляр Doge // ▸ Doge {saying: "не Window", getThis: ƒ} // ▸ Doge {saying: "не Window", getThis: ƒ}
get_this() по-прежнему указывает на вновь созданный объект. Потому что именно он его вызвал.
А что если, мы вызовем Doge без ключевого слова new?
function Doge( saying ){ this.saying = saying this.get_this = function(){ console.log( this ) } this.get_this() console.log( this ) } Doge( 'Кто здесь?' ) // запускаем Doge как простую функцию // Получим в консоли: // ▸ Window {0: Window, window: Window, self: Window, …} // ▸ Window {0: Window, window: Window, self: Window, …}
Как мы видим функция была вызвана объектом window и она добавила в вызываемый объект window новое свойство saying
и метод get_this()
. Убедимся в этом, посмотрим значение свойства saying и вызовем метод get_this().
window. saying // "Кто здесь?" window.get_this() // Window {0: Window, window: Window, self: Window, …}
Итого про this
Значение this устанавливается в зависимости от того, как вызвана функция:
При вызове функции в качестве метода, контекстом будет вызываемый объект:
obj.func(...) // this = obj obj["func"](...) // this = obj
При обычном вызове, контекстом будет глобальный объект она будет вызвана без контекста:
func(...) // this = window (ES3) / undefined (ES5 - 'use strict')
При использовании ключевого слова new, создается новый чистый контекст.
new func() // this = {} (новый объект)
Как работает this, bind, call и apply в JavaScript
16 ноября, 2019 12:17 пп 4 490 views | Комментариев нетDevelopment, Java | Amber | Комментировать запись
Ключевое слово this – очень важное понятие в JavaScript, но в работе с ним особенно легко запутаться как новичкам, так и опытным разработчикам, много работавшим с другими языками программирования. В JavaScript this – это ссылка на объект. Объект, на который ссылается это ключевое слово, может неявно варьироваться в зависимости от того, является ли он глобальным. Также он может явно варьироваться в зависимости от использования методов-прототипов Function: bind, call и apply.
В этом мануале вы узнаете, как по контексту определить, на что неявно ссылается this, и как использовать методы bind, call и apply для явного определения значения this.
Неявный контекст
Существует четыре основных контекста, в которых можно неявно определить значение ключевого слова this:
- глобальный контекст
- как метод внутри объекта
- как конструктор в функции или классе
- как обработчик событий DOM
Глобальный контекст
В глобальном контексте this ссылается на глобальный объект. Когда вы работаете в браузере, глобальный контекст – это окно. Когда вы работаете в Node.js, глобальный контекст – это global.
Примечание: Если вы еще не знакомы с понятием области видимости в JavaScript, ознакомьтесь с нашим мануалом Переменные, области и поднятие переменных в JavaScript.
В качестве примеров мы будем использовать код в консоли браузера Developer Tools.
Читайте также: Использование консоли разработчика JavaScript
Если вы зарегистрируете значение this без какого-либо другого кода, вы увидите, к какому объекту относится this.
console.log(this)
Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}
Вы увидите, что this – это окно, которое является глобальным объектом браузера.
В статье Переменные, области и поднятие переменных в JavaScript мы говорили о том, что функции имеют собственный контекст для переменных. Сейчас можно подумать, что this будет следовать тем же правилам внутри функции, но это не так. Функция верхнего уровня сохранит ссылку 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.
За дополнительной информацией о строгом режиме и о том, какие изменения он вносит в отношении ошибок и безопасности, обращайтесь к документации на MDN.
Метод объекта
Метод – это функция объекта или задача, которую может выполнить объект. Метод использует this для ссылки на свойства объекта.
Читайте также: Объекты в JavaScript
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.symbol.
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 появился синтаксис класса.
Читайте также: Работа с классами в 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)
})
<button>Click me</button>
Как только вы вставите этот код в свой браузер, на странице появится кнопка с надписью «Click me». Если вы нажмете кнопку, вы увидите <button>Click me</button> в консоли, так как нажатие кнопки регистрирует элемент, который является самой кнопкой. Как видите, this ссылается на целевой элемент, который является элементом, к которому мы добавили прослушиватель событий.
Явный контекст
Во всех предыдущих примерах значение this определялось его контекстом –глобальным, в объекте, в функции или классе или в обработчике событий DOM. Но используя call, apply или bind, вы можете явно определить, на что ссылается this.
Трудно точно определить, когда нужно использовать call, apply или bind, так как это зависит от контекста вашей программы. Метод bind может быть особенно полезен, если вы хотите использовать события для доступа к свойствам одного класса в другом классе. Например, если вы хотите написать простую игру, вы можете разделить пользовательский интерфейс и ввод-вывод на один класс, а игровую логику и состояние – на другой. Так как игровая логика должна иметь доступ к вводу, например нажатию клавиши и кликам, вам нужно связать (bind) события, чтобы получить доступ к значению this класса игровой логики.
Здесь важно определить, к какому объекту относится this, что вы можете сделать неявно (этому вы научились в предыдущих разделах) или явно (с помощью трех методов, которые вы изучите сейчас).
Методы 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)
// or:
summary.apply(book)
"Brave New World was written by Aldous Huxley."
Теперь между book и summary существует связь. Давайте точно узнаем, на что ссылается this.
function printThis() {
console.log(this)
}
printThis.call(book)
// or:
whatIsThis.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, произойдет вот что:
longerSummary.apply(book, 'dystopian', 1932)
Uncaught TypeError: CreateListFromArrayLike called on non-object at <anonymous>:1:15
При использовании apply вы должны передать все аргументы в массиве.
longerSummary.apply(book, ['dystopian', 1932])
"Brave New World was written by Aldous Huxley. It is a dystopian novel written in 1932. "
Разница между передачей аргументов по отдельности или в массиве невелика, но об этом важно знать. Использовать метод apply иногда может быть проще и удобнее, так как он не требует изменения вызова функции, если некоторые детали параметров изменились.
Метод bind
И call, и apply являются одноразовыми методами – если вы вызываете метод с контекстом this, он примет его, но исходная функция останется неизменной.
В отдельных ситуациях вам может понадобиться несколько раз использовать метод с контекстом this другого объекта. В таком случае вы можете использовать метод bind для создания новой функции с явно привязанным this.
const braveNewWorldSummary = summary.bind(book)
braveNewWorldSummary()
"Brave New World was written by Aldous Huxley"
Каждый раз, когда в этом примере вы вызываете braveNewWorldSummary, он будет возвращать исходное значение this, привязанное к нему. Попытка связать с ним новый контекст this не удастся, поэтому вы всегда можете доверять связанной функции и получить ожидаемое значение this.
const braveNewWorldSummary = summary.bind(book)
braveNewWorldSummary() // Brave New World was written by Aldous Huxley.
const book2 = {
title: '1984',
author: 'George Orwell',
}
braveNewWorldSummary.bind(book2)
braveNewWorldSummary() // Brave New World was written by Aldous Huxley.
Хотя этот пример пытается связать braveNewWorldSummary еще раз, он сохраняет оригинальный контекст this.
Стрелочные функции
Стрелочные функции не имеют привязки this. Вместо этого они переходят на следующий уровень исполнения.
Читайте также: Определение функций в JavaScript
const whoAmI = {
name: 'Leslie Knope',
regularFunction: function() {
console.log(this.name)
},
arrowFunction: () => {
console.log(this.name)
},
}
whoAmI.regularFunction() // "Leslie Knope"
whoAmI. arrowFunction() // undefined
Стрелочные функции полезно использовать в тех случаях, когда ключевое слово 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, и вы не смогли бы использовать его для доступа к значению в классе без явного его связывания. Эта тактика часто используется в методах классов в таких средах, как React.
Заключение
В этой статье вы узнали о ключевом слове this в JavaScript и о множестве различных значений, которые оно могло бы иметь в неявном контексте и при явном связывании посредством методов bind, call и apply. Также вы знаете, как отсутствие привязки this в стрелочных функциях можно использовать для ссылки на другой контекст. Обладая этими знаниями, вы сможете определить значение this в своих программах.
Tags: DOM, JavascriptМноголикий this в JS
В этом посте я сделаю все возможное, чтобы объяснить одну из самых фундаментальных частей JavaScript: контекст выполнения. Если вы много используете JS-фреймворки, понимание «this» может сначала показаться приятным дополнением. Однако, если вы собираетесь серьезно относиться к программированию, понимание контекста абсолютно важно для того, чтобы быть JavaScript программистом.
Мы используем this во многом так же, как мы используем его на естественном языке. Мы предпочли бы написать «Моя мама посинела, это очень тревожно», вместо «Моя мама посинела. Становление моей мамы синей очень беспокоит». Зная контекст this, мы можем понять, что нас так беспокоит.
Попробуем соединить его с языком программирования. В JavaScript мы используем this как ярлык, ссылку. Он относится к объектам, переменным, и мы используем их в контексте.
Это очень тревожно, но не бойтесь. Через минуту все станет ясно.
Глобальный контекст
Что вы думаете, если кто-нибудь скажет: «Это очень тревожно»? Без какой-либо заметной причины, так же, как начало разговора, без контекста или введения. Скорее всего, вы начнете связывать это с чем-то вокруг или с последней ситуацией.
Это происходит в браузере постоянно. Сотни тысяч разработчиков используют this без контекста. Наш бедный браузер делает все возможное, чтобы соотнести this применительно к глобальному объекту, window в этом конкретном примере.
Вне любой функции в глобальном контексте выполнения this относится к глобальному контексту (объекту window).
Контекст функции
Чтобы снова обратиться к реальному примеру, контекст функции можно воспринимать как контекст предложения. «Моя мама посинела, это очень тревожно». Мы использовали this в предложении, поэтому мы знаем, что это значит, но мы можем использовать его и в разных предложениях. Например: «Ураган грядет, это очень тревожно». То же самое, но другой контекст и совершенно другое значение.
Контекст в JavaScript связан с объектами. Он ссылается на объект внутри выполняемой функции. this относится к объекту, в котором выполняется функция.
this определяется тем, как вызывается функция. Как вы можете видеть, все вышеупомянутые функции были вызваны в глобальном контексте.
Когда функция вызывается как метод объекта, она ссылается на объект, который вызывал метод.
True — Мы все еще находимся в глобальном контексте.
False — функция вызывается как метод объекта.
True — функция вызывается как метод объекта.
False — Функция вызывается как метод объекта y_obj, поэтому this его контекст.
Example 4
В строгом режиме (strict mode) правила разнятся. Контекст остается таким, каким он был установлен. В этом конкретном примере this не был определен, поэтому он остается undefined.
Example 5
Как и в предыдущем примере, функция вызывается как метод объекта, независимо от того, как он был определен.
Example 6
this является динамическим, то есть он может меняться от одного объекта к другому
Example 7
Мы можем назвать фрукты this по имени объекта.
Example 8
Итак, new измененяет правила. Оператор new создает экземпляр объекта. Контекст функции будет ссылаться на созданный экземпляр объекта.
Call, apply, bind
Реальный пример: «Это очень тревожит — факт того, что моя мама посинела».
Эти методы позволяют нам выполнять любую функцию в любом желаемом контексте. Посмотрим, как они работают, на примерах.
Example 1
xo xo - Мы назвали тест в глобальном контексте.
lorem ipsum - Используя вызов, мы вызываем тест в контексте foo.
lorem ipsum - Используя apply, мы вызываем тест в контексте foo.
Эти два метода позволяют выполнять функцию в любом желаемом контексте.
apply позволяет вызывать функцию с аргументами в виде массива, тогда как call требует, чтобы параметры были явно указаны.
Example 2
Undefined - В объекте документа нет переменной.
Undefined - В объекте документа нет переменной. В этой ситуации вызов не может изменить контекст.
15 - Мы создали новый объект {a: 15} и вызвали test в этом контексте.
Метод bind устанавливает постоянный контекст в требуемое значение.
После использования bind this неизменяемо даже при вызове call, apply или bind.
Стрелочные функции (ES6)
Стрелочные функции были введены как фича ES6. Их можно рассматривать как очень удобный инструмент. Однако, вы должны знать, что стрелочные функции работают иначе, чем обычные функции с точки зрения контекста. Посмотрим.
Example 1
Когда мы используем стрелочные функции, this сохраняет значение охватывающего лексического контекста.
Example 2
Обратите внимание на разницу между стрелочной и обычной функцией. Сo стрелочной функцией мы находимся в контексте window.
Можно сказать, что:
x => this.y равно function(x) {return this.y}.bind(this)
Стрелочная функция всегда имеет значение this и поэтому не может использоваться как конструктор. Этот последний пример иллюстрирует разницу.
Example 3
Что почитать
http://www.joshuakehn.com/2011/10/20/Understanding-JavaScript-Context.html
http://ryanmorr.com/understanding-scope-and-context-in-javascript/
https://hackernoon.com/execution-context-in-javascript-319dd72e8e2c
http://2ality.com/2012/04/arrow-functions.html
Присоединяйтесь к нашим каналам FrontEndDev и Web Stack в Telegram, чтобы не пропустить самое интересное!
Оригинал статьи The many faces of this in javascript
Donate
Это на JavaScript | Зелл Лью
21 июня 2017 г. Вас смущает ключевое слово это
в JavaScript? Поначалу это сбивает всех с толку, так что не беспокойтесь об этом. Ты не одинок.
Но это не значит, что можно вечно не понимать этого
. Он так часто используется в JavaScript и в обучающих программах повсюду, что рано или поздно вам нужно понять, что такое и
. Как только вы поймете это
, вы поймете, что это намного проще, чем вы думаете.
К концу этой статьи вы демистифицируете этот
для себя. Вы узнаете, что это такое, что он делает и как его использовать.
Итак, что это?
this
— это ключевое слово, значение которого меняется в зависимости от того, как вызывается функция. Существует шесть различных способов, где и
могут принимать новые значения. Они:
-
этот
в глобальном контексте -
это
в объекте строительства -
это
в методе объекта -
это
в простой функции -
это
в функции стрелки -
это
в прослушивателе событий
Вы можете задаться вопросом, что такое на
в каждом контексте и почему вообще нужно менять на
. Чтобы ответить на ваш вопрос, давайте посмотрим, как меняется на
в каждом из шести контекстов.
Это в глобальном контексте
Когда этот
вызывается вне какой-либо функции, в глобальном контексте по умолчанию используется объект Window
в браузере.
console.log(это) // ОкноПо умолчанию это объект окна в браузерах
Обычно вы не будете использовать this
в глобальном контексте, поэтому значение this
здесь не имеет большого значения. Перейдем к следующему контексту.
Это в объекте строительства
При создании нового экземпляра объекта с новое ключевое слово
, это
относится к экземпляру.
функция Человек (возраст) { this.age = возраст } const greg = новый человек (22) const thomas = новый человек (24) console.log(грег) // this.age = 22 console.log(thomas) // this.age = 24Это относится к экземпляру
. Вы можете видеть, что greg
является экземпляром Person
в приведенном выше коде. Теперь всякий раз, когда вы ссылаетесь на greg
, вы случайно не получите thomas
. Итак, установка этот экземпляр
имеет смысл.
Далее рассмотрим тесно связанный контекст — это
в объектном методе.
Это в методе объекта
Методы — это причудливые слова для функций, связанных с объектом, например:
(Примечание. Методы здесь определены с помощью литерала объекта ES6. Прочтите эту статью, если не знаете, что он делает).
пусть о = { // Метод метод () {} }
этот
внутри любого метода относится к самому объекту.
пусть о = { скажи это () { console.log(это) } } o.sayThis() // оthis относится к объекту
Поскольку this
относится к объекту, вы можете использовать методы для получения экземпляра объекта, например:
функция Человек (имя) { возвращаться { имя, получитьИмя () { вернуть это.имя } } } const zell = новый человек('Zell') const vincy = новый человек('Винси') console.log(zell.getName()) // Зелл
В этих двух контекстах объектов вы можете видеть, что измененное значение на этот
позволяет вам получить правильный экземпляр, который является основой объектно-ориентированного программирования. Хотя это тема для другого дня.
Давайте перейдем к следующему контексту.
Это в простой функции
Простые функции — это функции, которые вы очень хорошо знаете; как тот, что ниже. Анонимные функции, написанные в той же форме, также считаются простыми функциями.
функция привет () { // скажи привет! }
В браузерах этот
всегда устанавливается на Окно
в простой функции. То же самое верно, даже если вы вызываете простую функцию в методе объекта.
функция простая функция () { console.log(это) } константа о = { скажи это () { простая функция () } } simpleFunction() // Окно o.sayThis() // Окно
К сожалению, смена на
для новичков неожиданна. Они ожидают, что и
останутся неизменными в методах объекта. Я тоже попался на этом.
Чтобы понять почему, рассмотрим следующий код. Здесь функция this.speakLeet
выполняется позже внутри функции setTimeout
.
константа о = { сделать что-то позже () { setTimeout (функция () { this.speakLeet() // Ошибка }, 1000) }, говоритьLeet () { console.log(`1337 15 4W350M3`) } }
К сожалению, приведенный выше код приводит к ошибке. Ошибка возникает из-за того, что этот
установлен на Window
в setTimeout
функция. Window
не имеет метода speakLeet
.
Одним из быстрых решений является создание переменной, в которой хранится ссылка на this
. Эту переменную часто называют self
или that
.
константа о = { сделать что-то позже () { константное само = это setTimeout (функция () { self.speakLeet() }, 1000) }, говоритьLeet () { console.log(`1337 15 4W350M3`) } }
Второй способ решить эту проблему — использовать новые стрелочные функции ES6, что приводит нас к следующему контексту.
Это в функциях стрелок
this
в стрелочной функции всегда совпадает с this
вокруг него (в его непосредственной области действия). Таким образом, если вы используете стрелочные функции в объектном методе, контекст this
остается объектом, а не Window
.
С стрелочными функциями пример speakLeet
можно записать следующим образом:
константа о = { сделать что-то позже () { setTimeout(() => this.speakLeet(), 1000) }, говоритьLeet () { console.log(`1337 15 4W350M3`) } }
(Подробнее о функциях стрелок читайте в этой статье)
Третий способ изменить значение на этот
в любой функции состоит в использовании либо bind
, либо call
, либо apply
. Мы рассмотрим bind
позже в этой статье, call
и apply
в другой раз. Но сначала давайте пройдемся по последнему контексту — слушателям событий.
Это в прослушивателях событий
этот
установлен на элемент, который запустил событие в прослушивателе событий:
пусть кнопка = document. querySelector('кнопка') button.addEventListener('щелчок', функция () { console.log(это) // кнопка })
При создании более сложных компонентов вы можете обнаружить, что создаете прослушиватели событий внутри методов.
функция LeetSpeaker (элемент) { возвращаться { слушатьклик () { elem.addEventListener('клик', функция () { // Делаем что-нибудь здесь }) } } }
Поскольку этот
относится к элементу в прослушивателе событий, если вам нужно активировать другой метод, вам необходимо предоставить ссылку на объект с помощью.
функция LeetSpeaker (элемент) { возвращаться { слушатьклик () { константное само = это elem.addEventListener('клик', функция () { self.speakLeet() }) }, говоритьLeet () { console.log(`1337 15 4W350M3`) } } }
В качестве альтернативы вы можете использовать функцию стрелки. Если вы сделаете это, вы все равно сможете получить элемент с event. currentTarget
.
функция LeetSpeaker (элемент) { возвращаться { слушатьклик () { elem.addEventListener('клик', e => { console.log(e.currentTarget) // элемент this.speakLeet() }) }, говоритьLeet () { console.log(`1337 15 4W350M3`) } } }
Но оба метода недостаточно хороши, чтобы помочь вам удалить прослушиватели событий, когда возникает необходимость, оба являются анонимными функциями.
Чтобы удалить прослушиватель событий, обратный вызов, передаваемый как второе значение, должен быть именованной функцией:
функция someFunction () { console.log('сделать что-нибудь') // Удаляет прослушиватель событий. document.removeEventListener('щелчок', someFunction) } document.addEventListener('щелчок', someFunction)
(здесь подробнее о обратных вызовах, если вам нужна помощь).
Если вам нужно this
для ссылки на объект в прослушивателе событий, вам нужно использовать bind
, чтобы вручную создать контекст this
.
функция LeetSpeaker (элемент) { возвращаться { слушатьклик () { this.listener = this.speakLeet.bind(это) elem.addEventListener('щелчок', this.listener) }, говоритьLeet (e) { константный элемент = e.currentTarget console.log(`1337 15 4W350M3`) elem.removeEventListener('щелчок', this.listener) } } }
Приведенный выше код может сбить вас с толку, если вы не понимаете bind
. Итак, прежде чем я покажу вам, что происходит, давайте отвлечемся и разберемся, что делает bind
.
Изменение с помощью привязки
bind
— это метод, присутствующий в каждой функции. Это позволяет вам изменить контекст на этот
. Этот метод принимает любое количество аргументов и возвращает связанную функцию.
функция сказать это () { console.log(это) } constboundFunc = sayThis.bind(/* аргументы...*/)
Первый параметр, который вы передаете в bind
, становится this
в связанной функции. После того, как вы создали связанную функцию, вы можете вызывать ее в любое время:
функция сказать это () { console.log(это) } constboundFunc = sayThis.bind({ хиппи: 'хипстер' }) связанная функция ()Bind помогает изменить это
Другие параметры, которые вы передаете в bind
, будут переданы в качестве аргументов исходной функции
функция sayParams (...args) { console.log(...аргументы) } constboundFunc = sayParams.bind(null, 1, 2, 3, 4, 5) связанная функция ()Другие параметры, переданные в bind, становятся аргументами функции
. Примечание: bind
не работает со стрелочными функциями.
Это действительно все, что вам нужно знать о bind
.
Теперь давайте вернемся к коду удаления прослушивателей событий и разберем, что происходит:
функция LeetSpeaker (элемент) { возвращаться { слушатьклик () { // Связывает this.speakLeet со ссылкой на экземпляр. // Устанавливает связанную функцию в this. listener, чтобы мы могли удалить ее позже. this.listener = this.speakLeet.bind(это) elem.addEventListener('щелчок', this.listener) }, говоритьLeet (e) { console.log(`1337 15 4W350M3`) // Получает элемент, чтобы мы могли удалить прослушиватель событий. константный элемент = e.currentTarget // Удаляет прослушиватель событий. elem.removeEventListener('щелчок', this.listener) } } }
Вот Codepen, чтобы вы могли увидеть код в действии.
См. Pen SpeakLeetOnce? от Zell Liew (@zellwk) на CodePen.
И это все, что вам нужно знать о этом
.
Давайте закругляться.
Подведение итогов
this
— ключевое слово в JavaScript. Он присутствует во многих фреймворках JavaScript, поэтому вам нужно знать, что он делает.
В этой статье вы узнали о шести различных контекстах, где это
принимает разные значения. Вы также узнали, как изменить контекст this
с помощью таких функций, как bind
. Кроме того, вы также научились правильно удалять прослушиватели событий.
Это все, что вам нужно знать об этом. Просто освойте концепции, изложенные в этой статье, и вы больше никогда не запутаетесь.
Прежде чем мы закончим статью, вы, возможно, слышали, как люди советовали вам не использовать вместо
, потому что это сбивает с толку. Я умоляю вас рассмотреть возможность прочтения этой статьи. Это может изменить ваш взгляд на изучение JavaScript.
Если у вас есть какие-либо вопросы, просто напишите мне в комментариях ниже 🙂
Если вам понравилась эта статья, поддержите меня, поделившись этой статьей в Твиттере или купив мне кофе 😉. Если вы заметили опечатку, я был бы признателен, если бы вы могли исправить ее на GitHub. Благодарю вас!
Многоликое `это` в javascript | by Michał Witkowski
В этом посте я постараюсь объяснить одну из самых фундаментальных частей JavaScript: контекст выполнения. Если вы часто используете JS-фреймворки, понимание «этого» на первый взгляд может показаться приятным дополнением. Однако, если вы собираетесь серьезно заняться программированием, понимание контекста абсолютно необходимо для того, чтобы стать программистом на JavaScript.
Мы используем это
почти так же, как мы используем его в естественном языке. Мы бы скорее написали «Моя мама посинела, это очень беспокоит». вместо «Моя мама посинела. Моя мама, которая синеет, очень беспокоится». Знание контекста и
позволяет нам понять, что нас так беспокоит.
Попробуем как-то связать это с языком программирования. В JavaScript мы используем и
как ярлык, ссылку. Он относится к объектам, переменным, и мы используем его в контексте.
Это очень тревожно, но не бойтесь. Через минуту все станет ясно.
Что вы подумаете, если кто-то скажет: «Это очень тревожно»? Без какого-либо заметного повода, просто как начало разговора, без контекста и вступления. Скорее всего, вы начнете связывать с этим
с чем-то вокруг или с последней ситуацией.
Это происходит с браузером МНОГО. Сотни тысяч разработчиков используют и
без контекста. Наш бедный браузер изо всех сил пытается понять это
в отношении глобального объекта, окна в этом конкретном примере.
Пример 1
[Веб-браузеры]
Вне любой функции в глобальном контексте выполнения, этот
относится к глобальному контексту (окну).
Еще раз обратимся к реальному примеру: контекст функции можно воспринимать как контекст предложения. «Моя мама посинела, это очень беспокоит». Мы использовали это в предложении, поэтому мы знаем, что это
означает, но мы можем использовать его и в других предложениях. Например: «Надвигается ураган, это очень беспокоит». Тот же это
, но другой контекст и совсем другой смысл.
Контекст в JavaScript связан с объектами. Он относится к объекту внутри выполняемой функции. this
относится к объекту, в котором выполняется функция.
Пример 1
this
определяется тем, как вызывается функция. Как видите, все вышеперечисленные функции были вызваны в глобальном контексте.
Пример 2
Когда функция вызывается как метод объекта, этот
устанавливается для объекта, для которого вызывается метод.
Пример 3
Верно
— Мы по-прежнему находимся в глобальном контексте.
False
— Функция вызывается как метод объекта.
True
— Функция вызывается как метод объекта.
False
— Функция вызывается как метод объекта y_obj, поэтому this
— это его контекст.
Пример 4
В строгом режиме правила другие. Контекст остается таким, каким он был установлен. В этом конкретном примере этот
не был определен, поэтому он остался неопределенным.
Пример 5
Как и в предыдущем примере, функция вызывается как метод объекта, независимо от того, как она была определена.
Пример 6
этот
является динамическим, то есть он может меняться от одного объекта к другому
Пример 7
Мы можем вызвать фрукты по этому
и по имени объекта.
Пример 8
Итак, новое изменяет правила. новый оператор
создает экземпляр объекта. Контекст функции будет установлен на созданный экземпляр объекта.
Пример из жизни: «Это очень тревожно, моя мама посинела».
Эти методы позволяют выполнять любую функцию в любом желаемом контексте. Давайте посмотрим, как они работают, на примерах.
Пример 1
xo xo
— Мы вызвали test в глобальном контексте.
lorem ipsum
— Используя вызов, мы вызываем тест в контексте foo.
lorem ipsum
— Используя команду apply, мы вызываем тест в контексте foo.
Эти два метода позволяют выполнять функцию в любом желаемом контексте.
apply
позволяет вызывать функцию с аргументами в виде массива, в то время как call требует явного указания параметров.
Пример 2
Не определено
— В объекте документа нет переменной.
Не определено
— В объекте документа нет переменной. В этой ситуации вызов не может изменить контекст.
15
— Мы создали новый объект {a:15} и в этом контексте вызвали тест.
Метод привязки постоянно устанавливает контекст в указанное значение.
После использования привязки это остается неизменным даже при вызове вызова, применения или привязки.
Стрелочные функции были представлены как функция в ES6. Их можно рассматривать как очень удобный инструмент. Однако вы должны знать, что стрелочные функции работают иначе, чем обычные функции с точки зрения контекста. Посмотрим.
Пример 1
Когда мы используем стрелочные функции, этот
сохраняет значение окружающего лексического контекста.
Пример 2
Обратите внимание на разницу между стрелкой и обычной функцией. Со стрелочной функцией мы находимся в контексте окна.
Мы можем сказать, что:
x => this.y равно function (x) { return this.y }.bind(this)
Стрелочная функция всегда связывала «это» и поэтому не может использоваться в качестве конструктора. Этот последний пример иллюстрирует разницу.
Пример 3
Как только вы узнаете разницу между функцией dynamic и лексической this
, дважды подумайте, прежде чем объявлять новую функцию. Если он вызывается как метод, используйте динамический this
. Если она вызывается как подпрограмма, используйте лексический код this
.
http://www.joshuakehn.com/2011/10/20/Understanding-JavaScript-Context.html
http://ryanmorr.com/understanding-scope-and-context-in-javascript/
https:/ /hackernoon.com/execution-context-in-javascript-319dd72e8e2c
http://2ality.com/2012/04/arrow-functions.html
Пусть ваши переменные JavaScript будут постоянными
Некоторое время назад вы могли прочитать о наших 10 лучших функциях ES6 (если вы еще не , посмотрите здесь). Вне всяких сомнений…
blog.pragmatists.com
Машинное обучение в браузере с TensorFlow.js
В последнее время все больше внимания уделяется искусственному интеллекту и машинному обучению. Казалось бы…
blog.pragmatists.com
Переосмысленное руководство по тестированию приложений React и Redux
Около года назад я опубликовал пост с похожим названием, объясняющий, как тестировать приложения React и Redux.
Это…blog.pragmatists.com
Javascript «это» Ключевое слово, как это работает? | Усама Эльмашад | tajawal
Одной из самых запутанных тем для JS-разработчиков является определение контекста, на который ссылается этот . Мы попытаемся объяснить 4 правила, которые помогут вам определить контекст это .
Ключевое слово это ведет себя в JavaScript иначе, чем в других языках. В JavaScript значение this не относится к функции, в которой оно используется, или к его области действия, а определяется главным образом контекстом вызова функции (context.function()) и местом ее вызова.
Как вы видите в приведенном выше примере, это в функции foo не относится к лексической области действия функции. Но обратитесь к глобальной области, потому что это контекст вызова функции (window.foo()).
Это наиболее распространенный случай, когда функция вызывает автономный вызов функции, как показано в примере ниже.
Как вы видите выше, поскольку мы вызываем myfunction() из контекста окна, это будет ссылаться на объект окна.
Мы определили переменную a в контексте окна, поэтому вывод вызова myFunction() будет равен 5, потому что это будет ссылаться на контекст окна, который является контекстом по умолчанию.
В этом случае объект, стоящий перед точкой, является тем, к чему будет привязано это ключевое слово.
Как вы видите выше, this будет ссылаться на объект obj, поэтому значение this.a будет равно 2.
Обратите внимание: если вы присвоите obj.foo переменной, эта переменная будет ссылаться на саму функцию
As мы видим, что когда вы вызываете john.greet(«Mark»), это будет ссылаться на объект john, так что this.name будет John
Но после этого при присваивании var fx = john.greet;
Таким образом, fx будет ссылкой на саму функцию приветствия , поэтому применяется привязка по умолчанию, и это будет ссылаться на Window.
В этом случае можно заставить вызов функции использовать определенный объект для этой привязки, не помещая ссылку на функцию свойства на объект. поэтому мы явно говорим функции, какой объект она должна использовать для этого — используя такие функции, как call, apply и bind
Функция apply похожа на call с тем отличием, что аргументы функции передаются в виде массива.
Функция связывания создает новую функцию, которая будет действовать как исходная функция, но с этим предопределенным.
Теперь функция welcomePerson является копией приветствия, но эта будет ссылаться на объект человека.
Таким образом, вы можете использовать bind для возврата функции, которую вы можете выполнить позже, но Call / apply используйте его, когда вам нужно немедленно вызвать функцию.
Для этой привязки существует последнее правило. Функция, которая вызывается с оператором new при выполнении кода new Foo(…), происходит следующее:
1- Создается пустой объект, на который ссылается эта переменная, наследующая прототип функции.
2- Свойства и методы добавляются к объекту, на который ссылается этот .
3- Вновь созданный объект, на который ссылается this, возвращается в конце неявно (если никакой другой объект не был возвращен явно).
Вызвав Foo() с новым перед ним, мы создали новый объект и установили этот новый объект как это для вызова foo().
Наивысший приоритет имеет новая привязка. Затем явное связывание и неявное связывание. Самый низкий приоритет имеет привязку по умолчанию.
Обычные функции подчиняются 4 правилам, которые мы только что рассмотрели. Но ES6 представляет особый вид функций, которые не используют эти правила:
Стрелочные функции имеют два основных преимущества.
1- У них более короткий синтаксис.
2- Y не привязывать значение это , когда вы используете функцию стрелки внутри другой функции:
этот всегда будет ссылаться на лексическую область действия и не будет влиять ни на одно правило из 4 правил, таких как явное связывание в приведенном выше примере, также если вы попытаетесь используйте новый , прежде чем функция стрелки выдаст ошибку. Потому что функции стрелки не могут работать с новым .
Таким образом, этот в стрелочной функции всегда будет принимать свое значение извне (лексическая область).
Здесь в forEach используется стрелочная функция, поэтому this.title в ней точно такое же, как и во внешнем методе showList. То есть: group.title.
Надеюсь, теперь вы сможете понять, о чем именно идет речь! Запомните несколько вещей:
1- Значение this обычно определяется контекстом выполнения функций.