- Вопрос или проблема
- Что вам следует знать о this
- Как ссылаться на правильный this
- Используйте стрелочные функции
- Не используйте this
- Явно задайте this обратного вызова – часть 1
- Установите this обратного вызова – часть 2
- Общая проблема: Использование методов объектов в качестве обработчиков обратных вызовов/событий
- Вот несколько способов доступа к родительскому контексту внутри дочернего контекста –
- 1. Используйте функцию bind()
- 2. Сохраните ссылку на контекст/this внутри другой переменной
- 3. Стрелочная функция
- Проблема с “контекстом”
- this в JavaScript:
- Пример в Node.js
- Ответ или решение
- Основные подходы к сохранению контекста this
- Пример с пользовательским объектом
- Заключение
Вопрос или проблема
У меня есть конструктор, который регистрирует обработчик событий:
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', function () {
alert(this.data);
});
}
// Заглушка для объекта транспортировки
var transport = {
on: function(event, callback) {
setTimeout(callback, 1000);
}
};
// вызывается как
var obj = new MyConstructor('foo', transport);
Тем не менее, я не могу получить доступ к свойству data
созданного объекта внутри обратного вызова. Похоже, что this
не относится к созданному объекту, а к другому.
Я также попробовал использовать метод объекта вместо анонимной функции:
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', this.alert);
}
MyConstructor.prototype.alert = function() {
alert(this.name);
};
но это вызывает те же проблемы.
Как мне получить доступ к правильному объекту?
</div><div class="s-prose js-post-body" itemprop="text">
Что вам следует знать о this
this
(также известный как “контекст”) — это специальное ключевое слово внутри каждой функции, и его значение зависит только от того, как функция была вызвана, а не от того, как/когда/где она была определена. Оно не подвержено влиянию лексических областей видимости, как другие переменные (кроме стрелочных функций, см. ниже). Вот некоторые примеры:
function foo() {
console.log(this);
}
// обычный вызов функции
foo(); // `this` будет ссылаться на `window`
// как метод объекта
var obj = {bar: foo};
obj.bar(); // `this` будет ссылаться на `obj`
// как конструктор
new foo(); // `this` будет ссылаться на объект, который наследует от `foo.prototype`
Чтобы узнать больше о this
, загляните в документацию MDN.
Как ссылаться на правильный this
Используйте стрелочные функции
ECMAScript 6 ввел стрелочные функции, которые можно рассматривать как лямбда-функции. У них нет собственного связывания this
. Вместо этого this
ищется в области видимости так же, как обычная переменная. Это означает, что вам не нужно вызывать .bind
. Это не единственное особенное поведение, которое у них есть, пожалуйста, обратитесь к документации MDN для получения дополнительной информации.
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', () => alert(this.data));
}
Не используйте this
<pНа самом деле вам не нужно получать доступ к this
в частности, а к объекту, на который он ссылается. Вот почему простое решение — это просто создать новую переменную, которая также ссылается на этот объект. Переменная может иметь любое имя, но распространенные варианты — это self
и that
.
function MyConstructor(data, transport) {
this.data = data;
var self = this;
transport.on('data', function() {
alert(self.data);
});
}
Поскольку self
— это обычная переменная, она подчиняется правилам лексической области видимости и доступна внутри обратного вызова. Это также имеет преимущество в том, что вы можете получить доступ к значению this
самого обратного вызова.
Явно задайте this
обратного вызова – часть 1
Может показаться, что у вас нет контроля над значением this
, потому что его значение устанавливается автоматически, но это на самом деле не так.
Каждая функция имеет метод .bind
[документация], который возвращает новую функцию с this
, привязанным к значению. Функция имеет точно такое же поведение, как функция, на которой вы вызывали .bind
, только this
был установлен вами. Не имеет значения, как или когда вызывается эта функция, this
всегда будет ссылаться на переданное значение.
function MyConstructor(data, transport) {
this.data = data;
var boundFunction = (function() { // скобки не обязательны
alert(this.data); // но могут улучшить читаемость
}).bind(this); // <- здесь мы вызываем `.bind()`
transport.on('data', boundFunction);
}
В этом случае мы связываем this
обратного вызова со значением this
MyConstructor
.
Примечание: При связывании контекста для jQuery используйте jQuery.proxy
[документация] вместо этого. Причина в том, что вам не нужно хранить ссылку на функцию при отвязывании обратного вызова события. jQuery обрабатывает это внутри.
Установите this
обратного вызова – часть 2
Некоторые функции/методы, которые принимают обратные вызовы, также принимают значение, на которое следует ссылаться this
обратного вызова. Это по сути то же самое, что привязывать его самостоятельно, но функция/метод делает это за вас. Array#map
[документация] — это такой метод. Его сигнатура:
array.map(callback[, thisArg])
Первый аргумент — это обратный вызов, а второй аргумент — значение, на которое должен ссылаться this
. Вот вымышленный пример:
var arr = [1, 2, 3];
var obj = {multiplier: 42};
var new_arr = arr.map(function(v) {
return v * this.multiplier;
}, obj); // <- здесь мы передаем `obj` как второй аргумент
Примечание: Возможность передачи значения для this
обычно упоминается в документации для этой функции/метода. Например, метод jQuery $.ajax
[документация] описывает опцию под названием context
:
This object will be made the context of all Ajax-related callbacks.
Общая проблема: Использование методов объектов в качестве обработчиков обратных вызовов/событий
Еще одним распространенным проявлением этой проблемы является использование метода объекта в качестве обработчика обратного вызова/события. Функции являются первоклассными гражданами в JavaScript, и термин “метод” — это просто разговорный термин для функции, которая является значением свойства объекта. Но эта функция не имеет конкретной связи со своим “содержащим” объектом.
Рассмотрим следующий пример:
function Foo() {
this.data = 42,
document.body.onclick = this.method;
}
Foo.prototype.method = function() {
console.log(this.data);
};
Функция this.method
назначена в качестве обработчика события щелчка, но если щелкнуть document.body
, значение, записываемое в журнал, будет undefined
, потому что внутри обработчика события this
ссылается на document.body
, а не на экземпляр Foo
.
Как уже упоминалось в начале, на что ссылается this
, зависит от того, как функция вызывается, а не от того, как она определяется.
Если бы код был следующим, было бы более очевидно, что функции нет неявной ссылки на объект:
function method() {
console.log(this.data);
}
function Foo() {
this.data = 42,
document.body.onclick = this.method;
}
Foo.prototype.method = method;
Решение такое же, как упоминалось выше: Если возможно, используйте .bind
, чтобы явно связать this
с конкретным значением.
document.body.onclick = this.method.bind(this);
или явно вызывайте функцию как “метод” объекта, используя анонимную функцию в качестве обратного вызова/обработчика события и присваивая объект (this
) другой переменной:
var self = this;
document.body.onclick = function() {
self.method();
};
или используйте стрелочную функцию:
document.body.onclick = () => this.method();
</div><div class="s-prose js-post-body" itemprop="text">
Вот несколько способов доступа к родительскому контексту внутри дочернего контекста –
- Вы можете использовать функцию
bind()
. - Сохраните ссылку на контекст/this внутри другой переменной (см. пример ниже).
- Используйте стрелочные функции ES6 Arrow.
- Измените код, проектирование функций и архитектуру – для этого вам следует владеть шаблонами проектирования в JavaScript.
1. Используйте функцию bind()
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', ( function () {
alert(this.data);
}).bind(this) );
}
// Заглушка для объекта транспортировки
var transport = {
on: function(event, callback) {
setTimeout(callback, 1000);
}
};
// вызывается как
var obj = new MyConstructor('foo', transport);
Если вы используете Underscore.js – http://underscorejs.org/#bind
transport.on('data', _.bind(function () {
alert(this.data);
}, this));
2. Сохраните ссылку на контекст/this внутри другой переменной
function MyConstructor(data, transport) {
var self = this;
this.data = data;
transport.on('data', function() {
alert(self.data);
});
}
3. Стрелочная функция
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', () => {
alert(this.data);
});
}
</div><div class="s-prose js-post-body" itemprop="text">
Всё дело в “магическом” синтаксисе вызова метода:
object.property();
Когда вы получаете свойство из объекта и вызываете его одним махом, объект будет контекстом для метода. Если вы вызываете тот же метод, но в отдельных шагах, контекст является глобальной областью (окно) вместо этого:
var f = object.property;
f();
Когда вы получаете ссылку на метод, она больше не связана с объектом. Это просто ссылка на обычную функцию. То же самое происходит, когда вы получаете ссылку для использования в качестве обратного вызова:
this.saveNextLevelData(this.setAll);
Вот где вы бы привязали контекст к функции:
this.saveNextLevelData(this.setAll.bind(this));
Если вы используете jQuery, вы должны использовать метод $.proxy
, так как bind
не поддерживается во всех браузерах:
this.saveNextLevelData($.proxy(this.setAll, this));
</div><div class="s-prose js-post-body" itemprop="text">
Вы должны знать о ключевом слове “this”.
На мой взгляд, вы можете реализовать “this” тремя способами
(Self|Стрелочная функция|Метод Bind)
Ключевое слово this
функции ведет себя немного иначе в JavaScript по сравнению с другими языками.
У него также есть некоторые отличия между строгим и нестрогим режимом.
В большинстве случаев значение этого определяется тем, как функция вызывается.
Его нельзя установить присвоением во время выполнения, и оно может быть разным каждый раз, когда функция вызывается.
ES5 ввел метод bind(), чтобы установить значение this
функции независимо от того, как она вызывается,
А ES2015 ввел стрелочные функции, которые не предоставляют своего собственного связывания this
(они сохраняют это значение окружающего lexical context).
Метод1: Self – Self используется для сохранения ссылки на оригинальный this
, даже когда контекст изменяется. Это техника, часто используемая в обработчиках событий (особенно в замыканиях).
Ссылка: this
function MyConstructor(data, transport) {
this.data = data;
var self = this;
transport.on('data', function () {
alert(self.data);
});
}
Метод2: Стрелочная функция – Выражение стрелочной функции является синтаксически компактной альтернативой обычному выражению функции, хотя без своей собственной привязки к ключевым словам this
, arguments
, super
или new.target
.
Выражения стрелочной функции плохо подходят в качестве методов, и их нельзя использовать в качестве конструкторов.
Ссылка: Выражения стрелочных функций
function MyConstructor(data, transport) {
this.data = data;
transport.on('data',()=> {
alert(this.data);
});
}
Метод 3: Bind – Метод bind() создает новую функцию, которая, когда вызывается, имеет ключевое слово this
, установленное в предоставленное значение с заданной последовательностью аргументов перед любыми переданными при вызове новой функции.
Ссылка: Function.prototype.bind()
function MyConstructor(data, transport) {
this.data = data;
transport.on('data',(function() {
alert(this.data);
}).bind(this);
</div><div class="s-prose js-post-body" itemprop="text">
Проблема с “контекстом”
Термин “контекст” иногда используется для обозначения объекта, на который ссылается this. Его использование неуместно, потому что оно не соответствует семантически или технически ECMAScript’s this.
“Контекст” означает обстоятельства, окружающие что-то, что добавляет значение, или некоторую предшествующую и последующую информацию, которая придает дополнительное значение. Термин “контекст” используется в ECMAScript для обозначения контекста выполнения, который охватывает все параметры, область видимости и this в пределах области видимости некоторого выполняемого кода.
Это показано в разделе 10.4.2 ECMA-262:
Установить связку this в такое же значение, как и связка this вызывающего контекста выполнения
Что ясно указывает на то, что this является частью контекста выполнения.
Контекст выполнения предоставляет окружающую информацию, которая добавляет значение к исполняемому коду. Он включает в себя гораздо больше информации, чем просто thisBinding.
Значение this не является “контекстом”. Это всего лишь одна часть контекста выполнения. По сути, это локальная переменная, которую можно установить с помощью вызова любого объекта, а в строгом режиме — любого значения вообще.
</div><div class="s-prose js-post-body" itemprop="text">
Во-первых, необходимо четко понять scope
и поведение ключевого слова this
в контексте scope
.
this
& scope
:
Существуют два типа областей видимости в JavaScript. Это:
-
Глобальная область видимости
-
Область видимости функции
Кратко, глобальная область видимости относится к объекту window. Переменные, объявленные в глобальной области видимости, доступны отовсюду.
С другой стороны, область видимости функции находится внутри функции. Переменная, объявленная внутри функции, не может быть доступна из внешнего мира в обычном режиме.
Ключевое слово this
в глобальной области видимости ссылается на объект window. this
внутри функции также ссылается на объект window. Таким образом, this
всегда будет ссылаться на окно, пока мы не найдем способ манипулировать this
, чтобы указать на контекст по своему выбору.
--------------------------------------------------------------------------------
- -
- Глобальная область видимости -
- (глобально "this" ссылается на объект window) -
- -
- function outer_function(callback){ -
- -
- // Внешняя область видимости функции -
- // Внутри внешней функции ключевое слово "this" -
- // ссылается на объект window -
- callback() // "this" внутри обратного вызова также ссылается на объект window -
- } -
- -
- function callback_function(){ -
- -
- // Функция, которая будет передана в качестве обратного вызова -
- -
- // Здесь "THIS" также ссылается на объект window -
- } -
- -
- outer_function(callback_function) -
- // Вызов с обратным вызовом -
- -
--------------------------------------------------------------------------------
Разные способы манипулирования this
внутри функций обратного вызова:
Здесь у меня есть конструктор с именем Person. У него есть свойство name
и четыре метода с именами sayNameVersion1
, sayNameVersion2
, sayNameVersion3
и sayNameVersion4
. Все четыре из них имеют одну конкретную задачу. Принять обратный вызов и вызвать его. У обратного вызова есть конкретная задача — записать в журнал свойство имени экземпляра конструктора Person.
function Person(name){
this.name = name
this.sayNameVersion1 = function(callback){
callback.bind(this)()
}
this.sayNameVersion2 = function(callback){
callback()
}
this.sayNameVersion3 = function(callback){
callback.call(this)
}
this.sayNameVersion4 = function(callback){
callback.apply(this)
}
}
function niceCallback(){
// Функция, которая будет использована в качестве обратного вызова
var parentObject = this
console.log(parentObject)
}
Теперь давайте создадим экземпляр из конструктора Person и вызовем разные версии метода sayNameVersionX
(X указывает на 1, 2, 3, 4) с niceCallback
, чтобы увидеть, сколько способов мы можем манипулировать this
внутри обратного вызова для ссылки на экземпляр person
.
var p1 = new Person('zami') // Создать экземпляр конструктора Person
То, что делает bind, — это создать новую функцию с ключевым словом this
, установленным в предоставленное значение.
sayNameVersion1
и sayNameVersion2
используют bind для манипуляции this
функции обратного вызова.
this.sayNameVersion1 = function(callback){
callback.bind(this)()
}
this.sayNameVersion2 = function(callback){
callback()
}
Первый связывает this
с обратным вызовом внутри самого метода. А для второго обратный вызов передается с объектом, привязанным к нему.
p1.sayNameVersion1(niceCallback) // просто передайте обратный вызов, и связывание происходит внутри метода sayNameVersion1
p1.sayNameVersion2(niceCallback.bind(p1)) // использует bind перед передачей обратного вызова
Первый аргумент
метода call
используется как this
внутри функции, которая вызывается с call
, прикрепленным к ней.
sayNameVersion3
использует call
, чтобы манипулировать this
и ссылаться на объект person, который мы создали, вместо объекта window.
this.sayNameVersion3 = function(callback){
callback.call(this)
}
И вызывается следующим образом:
p1.sayNameVersion3(niceCallback)
Аналогично call
, первый аргумент apply
ссылается на объект, который будет указывать ключевое слово this
.
sayNameVersion4
использует apply
, чтобы манипулировать this
, чтобы ссылаться на объект person.
this.sayNameVersion4 = function(callback){
callback.apply(this)
}
И вызывается следующим образом. Просто передается обратный вызов,
p1.sayNameVersion4(niceCallback)
</div><div class="s-prose js-post-body" itemprop="text">
Мы не можем привязать this
к setTimeout()
, так как он всегда выполняется с глобальным объектом (Window). Если вы хотите получить доступ к контексту this
в функции обратного вызова, тогда, используя bind()
к функции обратного вызова, мы можем этого добиться:
setTimeout(function(){
this.methodName();
}.bind(this), 2000);
</div><div class="s-prose js-post-body" itemprop="text">
Вопрос касается того, как ведет себя ключевое слово this
в JavaScript. this
ведет себя по-разному, как показано ниже:
- Значение
this
обычно определяется контекстом выполнения функции. - В глобальной области
this
ссылается на глобальный объект (объектwindow
). - Если строгий режим включен для любой функции, то значение
this
будетundefined
, так как в строгом режиме глобальный объект относится кundefined
вместо объектаwindow
. - Объект, стоящий перед точкой, — это к чему будет привязано ключевое слово
this
. - Мы можем явно установить значение
this
с помощьюcall()
,bind()
иapply()
. - Когда используется ключевое слово
new
(конструктор),this
привязывается к создаваемому новому объекту. - Стрелочные функции не привязывают
this
— вместо этогоthis
привязывается лексически (т.е. на основе оригинального контекста).
Как указывают большинство ответов, мы можем использовать стрелочную функцию или метод bind()
или переменную Self. Я бы процитировал пункт о лямбдах (стрелочная функция) из Google JavaScript Style Guide
Предпочитайте использование стрелочных функций перед f.bind(this), и особенно перед
goog.bind(f, this). Избегайте написания const self = this. Стрелочные функции
особенно полезны для обратных вызовов, которые иногда неожиданно передают
дополнительные аргументы.
Google ясно рекомендует использовать лямбды вместо bind или const self = this
Таким образом, лучшее решение — использовать лямбды следующим образом:
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', () => {
alert(this.data);
});
}
Ссылки:
</div><div class="s-prose js-post-body" itemprop="text">
В настоящее время есть еще один возможный подход, если в коде используются классы.
С поддержкой полей класса возможно сделать это следующим образом:
class someView {
onSomeInputKeyUp = (event) => {
console.log(this); // Это ссылается на правильное значение
// ....
someInitMethod() {
//...
someInput.addEventListener('input', this.onSomeInputKeyUp)
Конечно, под капотом это все старые добрые стрелочные функции, которые привязывают контекст, но в этой форме это выглядит гораздо яснее, чем явное связывание.
Поскольку это предложение на стадии 3, вам понадобятся Babel и соответствующий плагин Babel, чтобы обработать его на данный момент (08/2018).
</div><div class="s-prose js-post-body" itemprop="text">
Другим подходом, который является стандартным способом с DOM2 для привязывания this
внутри слушателя события, который позволяет вам всегда удалять слушателя (среди других преимуществ), является метод handleEvent(evt)
интерфейса EventListener
:
var obj = {
handleEvent(e) {
// всегда верно
console.log(this === obj);
}
};
document.body.addEventListener('click', obj);
Подробную информацию о использовании handleEvent
можно найти здесь: DOM handleEvent: кроссплатформенный стандарт с 2000 года
</div><div class="s-prose js-post-body" itemprop="text">
Я сталкивался с проблемой функции Ngx
линейного графика xAxisTickFormatting
, которая вызывалась из HTML следующим образом: [xAxisTickFormatting]="xFormat"
.
Я не мог получить доступ к переменной своего компонента из объявленной функции. Это решение помогло мне разрешить проблему, чтобы найти правильный this
.
Вместо использования функции таким образом:
xFormat (value): string {
return value.toString() + this.oneComponentVariable; //дает неправильный результат
}
Используйте это:
xFormat = (value) => {
// console.log(this);
// теперь у вас есть доступ к переменным вашего компонента
return value + this.oneComponentVariable
}
</div><div class="s-prose js-post-body" itemprop="text">
this
в JavaScript:
Значение this
в JavaScript на 100% определяется тем, как вызывается функция, а не тем, как она определена. Мы можем относительно легко найти значение this
по ‘правилу слева от точки’:
- Когда функция создается с помощью ключевого слова function, значение
this
— это объект слева от точки функции, которая вызывается - Если слева от точки нет объекта, то значение
this
внутри функции часто является глобальным объектом (глобальный
в Node.js иокно
в браузере). Я бы не рекомендовал использовать здесь ключевое словоthis
, так как это менее явно, чем использование чего-то вродеwindow
! - Существуют определенные конструкции, такие как стрелочные функции и функции, созданные с использованием
Function.prototype.bind()
, которые могут зафиксировать значениеthis
. Это исключения из правила, но они действительно полезны для фиксации значенияthis
.
Пример в Node.js
module.exports.data="module data";
// Это вне функции в node ссылается на объект module.exports
console.log(this);
const obj1 = {
data: "obj1 data",
met1: function () {
console.log(this.data);
},
met2: () => {
console.log(this.data);
},
};
const obj2 = {
data: "obj2 data",
test1: function () {
console.log(this.data);
},
test2: function () {
console.log(this.data);
}.bind(obj1),
test3: obj1.met1,
test4: obj1.met2,
};
obj2.test1();
obj2.test2();
obj2.test3();
obj2.test4();
obj1.met1.call(obj2);
Вывод:
Позвольте мне познакомить вас с выводами один за другим (игнорируя первый журнал, начиная со второго):
this
— этоobj2
из-за правила слева от точки, мы можем видеть, какtest1
вызываетсяobj2.test1();
.obj2
находится слева от точки, и таким образом значениеthis
.- Несмотря на то, что
obj2
слева от точки,test2
связан сobj1
с помощью методаbind()
. Значениеthis
— этоobj1
. obj2
находится слева от точки из вызываемой функции:obj2.test3()
. Поэтомуobj2
будет значениемthis
.- В этом случае:
obj2.test4()
obj2
находится слева от точки. Однако стрелочные функции не имеют собственного связыванияthis
. Поэтому оно будет привязано к значениюthis
внешнего контекста, которым являетсяmodule.exports
, объект, который был записан в журнале в начале. - Мы также можем определить значение
this
с помощью функцииcall
. Здесь мы можем передать желаемое значениеthis
в качестве аргумента, которое являетсяobj2
в этом случае.
</div><div class="s-prose js-post-body" itemprop="text">
Некоторые другие люди упомянули, как использовать метод .bind(), но в частности, вот как вы можете использовать его с .then(), если кто-то сталкивается с трудностями в их совместной работе:
someFunction()
.then(function(response) {
//'this' не был доступен здесь ранее, но теперь он есть
}.bind(this))
Как упоминалось в комментариях, альтернативой будет использование стрелочной функции, которая не имеет своего собственного значения ‘this’
someFunction()
.then((response)=>{
//'this' всегда был доступен здесь
})
</div><div class="s-prose js-post-body" itemprop="text">
Вы можете использовать стрелочную функцию, чтобы избежать проблемы с this
.
const functionToTest = (dataToSet , transport) => {
this.dataToSet = dataToSet ;
transport.on('dataToSet ', () => {
console.log(this.dataToSet);
});
}
</div><div class="s-prose js-post-body" itemprop="text">
Ответ на 2024 год
Много информации здесь довольно устарело. Этот ответ сосредоточен на том, как разрешить в 2024+
- Используйте экземпляры при вызове функции
- никогда не вынимайте функцию “из ее экземпляра”. Если вам нужно вызвать эту функцию позже, просто сохраните весь экземпляр, например, вот так:
class A {
constructor(id) {
this.id = id;
}
someFunction(){
console.log(this);
}
}
const a1 = new A(5);
const a2 = new A(20);
const arr = [a1, a2];
// некоторый бизнес-логика
arr.forEach(a => a.someFunction());
- Не злоупотребляйте
this
Не используйте this
и классы, если это не необходимо. Когда вы подключаете файлы, такие как репозитории, контроллеры, сервисы, это по умолчанию создает синглетоны, что является тем, что вы хотите (это по сути похоже на @Autowired в Java, но нативно). А синглетоны не требуют классов и “this”, что бы вам ни нужно было определить, определите на корне файла. Затем просто экспортируйте функции/переменные, которые вам нужны. Если вы не используете this
, вы не можете столкнуться с проблемами с this
.
Например, somethingService.js
может выглядеть похожим образом:
const someDefaultValue = 10;
export function doSomething() {
console.log(someDefaultValue);
}
Тогда вам не нужно думать о том, как использовать doSomething()
как somethingService.doSomething()
или просто doSomething()
напрямую. (это будет работать одинаково)
- Используйте apply/call/bind
Лично я не сторонник использования apply
(или call
или bind
, что по сути то же самое, просто немного иначе называют) в широком смысле, исходя из моего опыта, это может принести еще больше хаоса в код. Но могут быть случаи, когда вам это может понадобиться, поэтому я перечислю это здесь. Вы можете по сути “внедрить” this
с любым объектом, который вам нравится внутри функции.
class Simple {
constructor(id) {
this.id = id;
}
showThis(text) {
console.log(this, ` ** ${text} **`)
}
}
const simple = new Simple(25);
const showThisFn = simple.showThis;
showThisFn("используя это без apply");
showThisFn.apply(simple, ["используя это с apply"])
showThisFn.call(simple, "используя это с call - по сути то же самое, только синтаксическая разница сегодня")
boundShowThisFn = showThisFn.bind(simple);
boundShowThisFn("теперь это связано с функцией, так что вам не нужно указывать это при вызове");
- Используйте стрелочную функцию => правильно
Если у вас есть функция внутри функции и вы используете this
, стрелочная функция передаст контекст this
так, как вы бы ожидали. Использование ключевого слова function
изменит контекст this
.
(по сути, используйте стрелочные функции, когда это возможно, и вы в безопасности)
class A {
constructor(baseValue) {
this.baseValue = baseValue;
}
doMathArrowFunction(arr){
arr.forEach(item => {
console.log(`Стрелочная функция: элемент ${item} и this?.baseValue ${this?.baseValue}`);
console.log(item + this?.baseValue);
});
}
doMathOldWay(arr) {
arr.forEach(function(item) {
console.log(`Старая функция: элемент ${item} и this?.baseValue ${this?.baseValue}`);
console.log(item + this?.baseValue);
});
}
}
const x = [1,2];
const a = new A(10);
a.doMathArrowFunction(x);
a.doMathOldWay(x);
Ранее (до появления стрелочных функций) это часто решалось с использованием ключевых слов self/that
. Если вы столкнетесь с этим и хотите узнать больше – проверьте принятый ответ в этой теме.
Вот как я решил проблему
class myClass
{
constructor(parent)
{
this.callback = (function() {
this.callbackFunctionOfParent();
}).bind(parent);
}
callCallback() {
this.callback();
}
}
class Class2
{
constructor()
{
this.Name = "CLASS 2";
this.test = new myClass(this);
this.test.callCallback();
}
callbackFunctionOfParent()
{
console.log("родитель: " + this.Name);
}
}
var c2 = new Class2;
.
Ответ или решение
Чтобы получить доступ к правильному значению this
внутри коллбека в JavaScript, необходимо учитывать, что this
зависит от контекста вызова функции. Ваша проблема заключается в том, что обычные функции в JavaScript теряют свой контекст при передаче их как коллбеки. Давайте подробнее рассмотрим, как можно решить эту проблему.
Основные подходы к сохранению контекста this
-
Использование стрелочных функций
Стрелочные функции в JavaScript не имеют собственного контекста
this
; вместо этого они наследуют его из родительской области видимости. Это означает, чтоthis
внутри стрелочной функции будет ссылаться на контекст, в котором она была определена.Пример:
function MyConstructor(data, transport) { this.data = data; transport.on('data', () => { alert(this.data); }); }
-
Использование метода
bind()
Метод
bind()
позволяет привязатьthis
к определенному объекту, создавая новую функцию. После этого, когда эта функция вызывается,this
внутри нее будет ссылаться на указанное значение.Пример:
function MyConstructor(data, transport) { this.data = data; transport.on('data', function() { alert(this.data); }.bind(this)); }
-
Сохранение контекста в переменную
Этот метод заключается в создании переменной (обычно называемой
self
илиthat
), которой присваивается текущее значениеthis
. Вы можете использовать эту переменную внутри коллбека.Пример:
function MyConstructor(data, transport) { this.data = data; var self = this; transport.on('data', function() { alert(self.data); }); }
Пример с пользовательским объектом
Вот пример, который обобщает вышеизложенные подходы на пользовательском объекте:
function MyConstructor(data, transport) {
this.data = data;
// 1. Использование стрелочной функции
transport.on('data', () => {
alert(this.data);
});
// 2. Использование bind
transport.on('data', function() {
alert(this.data);
}.bind(this));
// 3. Сохранение контекста в переменную
var self = this;
transport.on('data', function() {
alert(self.data);
});
}
// Имитация объекта transport
var transport = {
on: function(event, callback) {
setTimeout(callback, 1000);
}
};
// Создание экземпляра
var obj = new MyConstructor('foo', transport);
Заключение
Важно понимать, как работает this
в JavaScript, и выбрать подходящий метод для его сохранения в зависимости от контекста вашей функции. Используйте стрелочные функции, если вам нужен доступ к this
из родительской функции, или метод bind()
, если вы хотите явно установить this
для вашей функции. В случае более старых решений сохранение this
в переменной также работает, но рекомендуется использовать более современные подходы для более чистого и менее подверженного ошибкам кода.
При выборе подхода учитывайте ситуацию и стили кодирования, принятые в вашей команде, чтобы обеспечить максимальную читаемость и предсказуемость кода.