Typescript "this" внутри метода класса

Я знаю, что это, наверное, больно основательно, но у меня тяжелое время обертывание вокруг меня.

class Main
{
     constructor()
     {
         requestAnimationFrame(this.update);  //fine    
     }

     update(): void
     {
         requestAnimationFrame(this.update);  //error, because this is window
     }

}

Кажется, что мне нужен прокси-сервер, поэтому скажем, используя JQuery

class Main
{
     constructor()
     {
         this.updateProxy = $.proxy(this.update, this);
         requestAnimationFrame(this.updateProxy);  //fine    
     }

     updateProxy: () => void
     update(): void
     {
         requestAnimationFrame(this.updateProxy);  //fine
     }

}

Но, исходя из фона ActionScript 3, я не совсем уверен, что здесь происходит. Извините, я не знаю, где начинается Javascript и заканчивается TypeScript.

updateProxy: () => void

И также, я не уверен, что я делаю это правильно. Последнее, что я хочу, это большая часть моего класса, у которого есть функция a(), к которой нужно получить доступ с помощью aProxy(), поскольку я чувствую, что я пишу одно и то же дважды? Это нормально?

Ответы

Ответ 1

Если вы хотите this захватить TypeScript способ сделать это через функции стрелок. Процитировать Андерса:

Функции this в стрелочках лексически ограничены

Вот как я люблю использовать это в своих интересах:

class test{
    // Use arrow functions
    func1=(arg:string)=>{
            return arg+" yeah" + this.prop;
    }
    func2=(arg:number)=>{
            return arg+10 + this.prop;
    }       

    // some property on this
    prop = 10;      
}

Посмотрите это на TypeScript Игровая площадка

Вы можете видеть, что в сгенерированном JavaScript this отображается вне вызова функции:

var _this = this;
this.prop = 10;
this.func1 = function (arg) {
    return arg + " yeah" + _this.prop;
};

поэтому значение this внутри вызова функции (которое может быть window) не будет использоваться.

Чтобы узнать больше: "Понимание this в TypeScript" (4:05) - YouTube

Ответ 2

Если вы напишете свои методы следующим образом, 'this' будет обрабатываться так, как вы ожидаете.

class Main
{
    constructor()
    {
        requestAnimationFrame(() => this.update());
    }

    update(): void
    {
        requestAnimationFrame(() => this.update());
    }
}

Другим вариантом было бы привязать 'this' к вызову функции:

class Main
{
    constructor()
    {
        requestAnimationFrame(this.update.bind(this));
    }

    update(): void
    {
        requestAnimationFrame(this.update.bind(this));
    }
}

Ответ 3

См. стр. 72 спецификации языка typescript https://github.com/Microsoft/TypeScript/blob/master/doc/TypeScript%20Language%20Specification.pdf?raw=true

Выражения со стрелкой

В примере

class Messenger {
 message = "Hello World";
 start() {
 setTimeout(() => alert(this.message), 3000);
 }
};
var messenger = new Messenger();
messenger.start();

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

Это фактический сгенерированный Javascript:

class Messenger {
 message = "Hello World";
 start() {
 var _this = this;
 setTimeout(function() { alert(_this.message); }, 3000);
 }
};

Ответ 4

Проблема возникает, когда вы передаете функцию в качестве обратного вызова. К моменту выполнения обратного вызова значение "this" могло быть изменено на Окно, элемент управления, вызывающий обратный вызов, или что-то еще.

Убедитесь, что вы всегда используете выражение лямбда в точке, где вы передаете ссылку на функцию, которая будет вызвана обратно. Например

public addFile(file) {
  this.files.push(file);
}
//Not like this
someObject.doSomething(addFile);
//but instead, like this
someObject.doSomething( (file) => addFile(file) );

Скомпилируется что-то вроде

this.addFile(file) {
  this.files.push(file);
}
var _this = this;
someObject.doSomething(_this.addFile);

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

Ответ 5

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

В Javascript, поскольку функции являются просто переменными, вы можете передавать их.

Пример:

var x = {
   localvar: 5, 
   test: function(){
      alert(this.localvar);
   }
};

x.test() // outputs 5

var y;
y.somemethod = x.test; // assign the function test from x to the 'property' somemethod on y
y.test();              // outputs undefined, this now points to y and y has no localvar

y.localvar = "super dooper string";
y.test();              // outputs super dooper string

Когда вы выполните следующее с помощью jQuery:

$.proxy(this.update, this);

То, что вы делаете, переопределяет этот контекст. За кулисами jQuery вас ожидает следующее:

$.proxy = function(fnc, scope){
  return function(){
     return fnc.apply(scope);  // apply is a method on a function that calls that function with a given this value
  }
};

Ответ 6

Очень поздно на вечеринку, но я думаю, что для будущих посетителей этого вопроса очень важно учесть следующее:

Другие ответы, в том числе принятый, упускают важный момент:

myFunction() { ... }

а также

myFunction = () => { ... }

не то же самое "за исключением того, что последний захватывает this ".

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

Быть завершенным:

myFunction = function() { ... }

будет такой же, как и второй синтаксис, но без захвата.

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

В этих случаях использование прокси или .bind() самом деле является правильным решением. (Хотя страдает читабельностью.)

Больше чтения здесь (не в первую очередь о TypeScript, но принципы стоят):

https://medium.com/@charpeni/arrow-functions-in-class-properties-might-not-be-as-great-as-we-think-3b3551c440b1

https://ponyfoo.com/articles/binding-methods-to-class-instance-objects

Ответ 7

Как насчет этого? Объявите глобальную переменную типа "myClass" и инициализируйте ее в конструкторе класса:

var _self: myClass;

class myClass {
    classScopeVar: string = "hello";

    constructor() {
        _self = this;
    }

    alerter() {
        setTimeout(function () {
            alert(_self.classScopeVar)
        }, 500);
    }
}

var classInstance = new myClass();
classInstance.alerter();

Примечание: важно НЕ использовать "я", так как оно уже является особым значением.