Переменная внутри setTimeout говорит, что она undefined, но когда она определена вне

У меня есть класс. Мне нужно выполнить некоторую работу в течение таймаута. Проблема, с которой я сталкиваюсь, - это переменная http внутри таймаута, которая говорит, что это undefined.

export class MyClass {

    http:Http:

    constructor(private http:Http) {
        this.http = http;
    }

    sendFriendRequest(){

    this.http.post( ...//http variable is defined here
           setTimeout(function(){
               this.http.post(...  //http is not defined here
        }
   }
}

Ответ 1

Причиной этого является то, что функция обратного вызова внутри setTimeout находится в другой лексической среде. Вот почему в ES6 + функции могут быть определены с помощью =>. Это значит, что код внутри функции имеет ту же область действия, что и функция.

Чтобы исправить это, вы можете использовать синтаксис ES6 +, вместо function(a,b,args) {...} используйте (a,b,args) => {...}:

setTimeout( () => {
  this.http.post(...)
});

или с синтаксисом ES5:

var root = this;

setTimeout(function(){
    root.http.post(...)
}

Надеюсь, это поможет!

Ответ 2

Здесь вы должны использовать функцию стрелки, чтобы сохранить существующее.

setTimeout(()=>{
   this.http.post(...  //http is not defined here
})

При этом this внутри функции привязывается к внешнему контексту. Это то же самое, что:

setTimeout(function(){
    this.http.post();
}.bind(this));

Ответ 3

В JavaScript ключевое слово this используется для доступа к context, в котором вызывается функция. Функции в JavaScript всегда вызывают с контекстом, вызывают ли они их с помощью синтаксиса .methodName() или без него, если только флаг 'use strict' не установлен в текущей области.

Когда функция вызывается без такого контекста:

myFunction()

контекст считается средой выполнения как глобальный объект окна (если не установлен флаг 'use strict', в этом случае контекст будет undefined.)

Примечание. При использовании ES6 с транспилером, таким как Babel, строгий режим устанавливается по умолчанию на выходе.

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

var myObj = {
    myFunc: function(){}
};

// myFunc invoked like this, the value of 'this' inside myFunc will be myObj.
myObj.myFunc();

Манипулировать 'this':

Вызов и применение

Вы всегда можете изменить контекст функции, вызвав ее с помощью методов .call или .apply. В этом случае у вас есть анонимная функция, которая не вызывается вами, а вызвана функцией setTimeout. Из-за этого вы не сможете воспользоваться .call или .apply.

Bind

Вместо этого вы можете создать новую функцию, которая имеет собственный контекст, используя метод .bind. Вызывая функцию .bind() для анонимной функции, будет возвращена новая функция, привязанная к вашему пользовательскому контексту 'this'. Таким образом, вы можете передать свою настраиваемую функцию привязки в качестве данных в setTimeout.

setTimeout(function(){
   // your code.
}.bind(this), 1000);

теперь внутри анонимной функции ключевое слово 'this' будет привязано к правильному значению.

Лексический 'this':

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

setTimeout(() => {
    // Hey I can access 'this' in here!
}, 1000);

Сохранение ссылки:

Если вы посмотрите на скомпилированный вывод из Babel, вы увидите, что Babel отслеживает контекст, сохраняя ссылки на 'this' с помощью _this1, _this2 и т.д.

Чтобы использовать этот метод самостоятельно, просто объявляйте новую переменную (она обычно использует "это" или "я" ) и получает доступ к значению, используя его внутри вашей анонимной функции, например:

var self = this;
setTimeout(function(){
    self.http.post...
}); 

Надеюсь, это поможет.

Для получения дополнительной информации у developer.mozilla.org есть хорошая статья, описывающая поведение 'this' внутри области функций.

Ответ 4

он не тот же this внутри setTimeout, когда вы используете function(){...

2 наиболее популярных способа для этой проблемы:

1) используйте дополнительную переменную для хранения вне "this"

var that = this;
this.http.post( ...//http variable is defined here
           setTimeout(function(){
               that.http.post(...  //http is not defined here
        }
   }

2) использовать функции стрелок

this.http.post( ...//http variable is defined here
           setTimeout(() => {
               that.http.post(...  //http is not defined here
        }
   }

Первый способ - это старый ES5, и вам не нужны никакие компиляторы, для версии ES6 (# 2) вам нужно будет использовать что-то вроде babel.

Найдите больше о функциях стрелок и babel здесь: https://babeljs.io/docs/learn-es2015/