Почему код внутри промиса выполняется, если в этом контексте `this` имеет значение `undefined`?

Вот переписанный вопрос с улучшенной ясностью и структурой:

Привожу фрагмент класса и добавляю к нему пояснения по следующим вопросам:

1) Почему в данном контексте значение `this` становится `undefined`? Я полагаю, что в стрелочных функциях `this` захватывает значение из внешней области видимости на момент их создания. Когда я вызываю метод `init` у экземпляра `database`, `this` должен указывать на этот экземпляр. Однако в строке `const dbname = this.config.dbname;` `this` оказывается `undefined`, как это видно в консоли браузера.

2) Несмотря на то, что `this` кажется `undefined`, переменные все-таки заполняются корректно. Это создаёт впечатление, что `this` всё же ссылается на экземпляр класса. Как это возможно?

3) В коде также возникает невозможно объяснимая ситуация, когда `this` соответствует `IDBOpenDBRequest`. Если в стрелочной функции, связанной с промисом, `this` должен указывать на экземпляр класса, то, получается, что лямбда для события `onupgradeneeded` использует `this` из внешней функции. Это ведет к тому, что в обработчике события `this` опять должен ссылаться на экземпляр, но это не так. Почему так происходит?

4) Как тогда может вызываться метод `createStorages()`, если `this` якобы ссылается на `IDBOpenDBRequest`? Как объяснить это несоответствие?
  • 4 сентября 2024 г. 13:12
Ответы на вопрос 3
Давайте разберем ваши вопросы по порядку, основываясь на типичных сценариях использования JavaScript, особенно в контексте классов и промисов.

### 1) Почему значение `this` становится `undefined`?

В JavaScript значение `this` зависит от способа вызова функции. Если функция, которую вы вызываете, не привязана к объекту (например, из-за того, что она вызвана как метод экземпляра класса), то значение `this` будет равно `undefined` или `window` в строгом режиме и нестрогом режиме соответственно. 

Когда вы вызываете метод `init`, в котором используется конструкция `this.config.dbname`, `this` должен указывать на экземпляр класса. Но при использовании обычных функций (например, в обработчиках событий), `this` может ссылаться на объект, который инициировал событие, и, если этот объект не является экземпляром класса, то у вас может быть `undefined`.

Если используете стрелочные функции, они захватывают значение `this` из области, в которой были объявлены. Поэтому, если в этой области `this` все еще равно экземпляру класса, то внутри стрелочной функции `this` будет по-прежнему ссылаться на экземпляр.

### 2) Почему переменные заполняются корректно?

Возможно, что вы находитесь в контексте, где `this` всё-таки указывает на экземпляр класса, либо вы используете стрелочную функцию в каком-то месте вашего кода, где это значение сохраняется. Также может быть, что переменные инициализируются до того, как происходит потеря контекста `this`, или результат обращения к `this` в этом месте кода имеет значение, отличающееся от `undefined`.

### 3) Почему `this` соответствует `IDBOpenDBRequest`?

При использовании конструктора `IDBOpenDBRequest` и его событий, таких как `onupgradeneeded`, `this` ссылается на объект, для которого это событие произошло, то есть на сам объект `IDBOpenDBRequest`. Это является стандартным поведением JavaScript.

Если вы используете стрелочную функцию в качестве обработчика, то `this` в ней будет ссылаться на внешнюю область видимости, где эта функция была объявлена, а не на `IDBOpenDBRequest`. Однако, если вы используете обычную функциональную запись (а не стрелочную), то `this` будет ссылаться на `IDBOpenDBRequest`.

### 4) Как может вызываться метод `createStorages()`, если `this` указывает на `IDBOpenDBRequest`?

Если вы вызываете метод, как `this.createStorages()`, внутри стрелочной функции, то они могут сработать, если значение `this` во внешнем контексте действительно ссылается на экземпляр класса, а не на `IDBOpenDBRequest`. Это ещё раз подчеркивает, насколько важно понимать отличие обычных и стрелочных функций в JavaScript. 

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

### Заключение

Непреднамеренные изменения контекста `this`, возникшие из-за меток, функций, обработчиков событий и использования стрелочных функций, являются распространенной проблемой в JavaScript. Я рекомендую использовать метод `.bind(this)` для явного связывания `this` к экземпляру класса, или же основываться на стрелочных функциях там, где они того требуют.
> 1) Почему this тут undefined? 
Это неправда

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

> 3) Почему this тут IDBOpenDBRequest?
Опять же неправда

> 4) Почему при этом вызывается метод createStorages() класса?
См. выше

ПС возможно вы что-то не так или не туда смотрите в дебаггере
1. this не undefined, undefined - поле класса config, вероятно потому что config не был передан в конструктор 
ну и дальше по списку все вытекает из первого пункта

В общем надо разобраться что такое this

https://developer.mozilla.org/ru/docs/Web/JavaScri...
https://habr.com/ru/companies/ruvds/articles/419371/
https://habr.com/ru/articles/785872/

Добавь в код
constructor(config) {
    this.config = config;
    console.log(this);
    console.log(this.config);
  }


где-то снаружи класса

let db = new Database();

db = new Database('config');


И посмотри что получится
Похожие вопросы