Как получить доступ к правильному 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">

Вот несколько способов доступа к родительскому контексту внутри дочернего контекста –

  1. Вы можете использовать функцию bind().
  2. Сохраните ссылку на контекст/this внутри другой переменной (см. пример ниже).
  3. Используйте стрелочные функции ES6 Arrow.
  4. Измените код, проектирование функций и архитектуру – для этого вам следует владеть шаблонами проектирования в 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. Это:

  1. Глобальная область видимости

  2. Область видимости функции

Кратко, глобальная область видимости относится к объекту 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:

То, что делает 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:

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

sayNameVersion3 использует call, чтобы манипулировать this и ссылаться на объект person, который мы создали, вместо объекта window.

this.sayNameVersion3 = function(callback){
    callback.call(this)
}

И вызывается следующим образом:

p1.sayNameVersion3(niceCallback)

apply:

Аналогично 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 ведет себя по-разному, как показано ниже:

  1. Значение this обычно определяется контекстом выполнения функции.
  2. В глобальной области this ссылается на глобальный объект (объект window).
  3. Если строгий режим включен для любой функции, то значение this будет undefined, так как в строгом режиме глобальный объект относится к undefined вместо объекта window.
  4. Объект, стоящий перед точкой, — это к чему будет привязано ключевое слово this.
  5. Мы можем явно установить значение this с помощью call(), bind() и apply().
  6. Когда используется ключевое слово new (конструктор), this привязывается к создаваемому новому объекту.
  7. Стрелочные функции не привязывают 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);
  });
}

Ссылки:

  1. https://medium.com/tech-tajawal/javascript-this-4-rules-7354abdb274c
  2. arrow-functions-vs-bind
</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 по ‘правилу слева от точки’:

  1. Когда функция создается с помощью ключевого слова function, значение this — это объект слева от точки функции, которая вызывается
  2. Если слева от точки нет объекта, то значение this внутри функции часто является глобальным объектом (глобальный в Node.js и окно в браузере). Я бы не рекомендовал использовать здесь ключевое слово this, так как это менее явно, чем использование чего-то вроде window!
  3. Существуют определенные конструкции, такие как стрелочные функции и функции, созданные с использованием 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);

Вывод:

Введите описание изображения сюда

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

  1. this — это obj2 из-за правила слева от точки, мы можем видеть, как test1 вызывается obj2.test1();. obj2 находится слева от точки, и таким образом значение this.
  2. Несмотря на то, что obj2 слева от точки, test2 связан с obj1 с помощью метода bind(). Значение this — это obj1.
  3. obj2 находится слева от точки из вызываемой функции: obj2.test3(). Поэтому obj2 будет значением this.
  4. В этом случае: obj2.test4() obj2 находится слева от точки. Однако стрелочные функции не имеют собственного связывания this. Поэтому оно будет привязано к значению this внешнего контекста, которым является module.exports, объект, который был записан в журнале в начале.
  5. Мы также можем определить значение 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+

  1. Используйте экземпляры при вызове функции
  • никогда не вынимайте функцию “из ее экземпляра”. Если вам нужно вызвать эту функцию позже, просто сохраните весь экземпляр, например, вот так:
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());
  1. Не злоупотребляйте this

Не используйте this и классы, если это не необходимо. Когда вы подключаете файлы, такие как репозитории, контроллеры, сервисы, это по умолчанию создает синглетоны, что является тем, что вы хотите (это по сути похоже на @Autowired в Java, но нативно). А синглетоны не требуют классов и “this”, что бы вам ни нужно было определить, определите на корне файла. Затем просто экспортируйте функции/переменные, которые вам нужны. Если вы не используете this, вы не можете столкнуться с проблемами с this.

Например, somethingService.js может выглядеть похожим образом:

const someDefaultValue = 10;

export function doSomething() {
   console.log(someDefaultValue);
}

Тогда вам не нужно думать о том, как использовать doSomething() как somethingService.doSomething() или просто doSomething() напрямую. (это будет работать одинаково)

  1. Используйте 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("теперь это связано с функцией, так что вам не нужно указывать это при вызове");
  1. Используйте стрелочную функцию => правильно

Если у вас есть функция внутри функции и вы используете 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

  1. Использование стрелочных функций

    Стрелочные функции в JavaScript не имеют собственного контекста this; вместо этого они наследуют его из родительской области видимости. Это означает, что this внутри стрелочной функции будет ссылаться на контекст, в котором она была определена.

    Пример:

    function MyConstructor(data, transport) {
       this.data = data;
       transport.on('data', () => {
           alert(this.data);
       });
    }
  2. Использование метода bind()

    Метод bind() позволяет привязать this к определенному объекту, создавая новую функцию. После этого, когда эта функция вызывается, this внутри нее будет ссылаться на указанное значение.

    Пример:

    function MyConstructor(data, transport) {
       this.data = data;
       transport.on('data', function() {
           alert(this.data);
       }.bind(this));
    }
  3. Сохранение контекста в переменную

    Этот метод заключается в создании переменной (обычно называемой 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 в переменной также работает, но рекомендуется использовать более современные подходы для более чистого и менее подверженного ошибкам кода.

При выборе подхода учитывайте ситуацию и стили кодирования, принятые в вашей команде, чтобы обеспечить максимальную читаемость и предсказуемость кода.

Оцените материал
Добавить комментарий

Капча загружается...