У меня есть класс TypeScript с функцией, которую я намерен использовать в качестве обратного вызова:

removeRow(_this:MyClass): void {
    ...
    // 'this' is now the window object
    // I must use '_this' to get the class itself
    ...
}

Я передаю это в другую функцию

this.deleteRow(this.removeRow);

Который, в свою очередь, вызывает метод jQuery Ajax, который в случае успеха вызывает обратный вызов следующим образом:

deleteItem(removeRowCallback: (_this:MyClass) => void ): void {
    $.ajax(action, {
        data: { "id": id },
        type: "POST"
    })
    .done(() => {
        removeRowCallback(this);
    })
    .fail(() => {
        alert("There was an error!");
    });
}

Единственный способ сохранить ссылку «this» на мой класс - передать его в функцию обратного вызова, как показано выше. Это работает, но это код штанов. Если я не подключу «this» таким образом (извините), то любая ссылка на это в методе обратного вызова вернется к объекту Window. Поскольку я все время использую функции со стрелками, я ожидал, что «this» будет сам класс, как и везде в моем классе.

Кто-нибудь знает, как передавать обратные вызовы в TypeScript, сохраняя лексическую область видимости?

40
Ralph Lavelle 23 Янв 2013 в 07:27

3 ответа

Используйте .bind () для сохранения контекста в обратном вызове.

Пример рабочего кода:

window.addEventListener(
  "resize",
  (()=>{this.retrieveDimensionsFromElement();}).bind(this)
)

Код в исходном вопросе станет примерно таким:

$.ajax(action, {
    data: { "id": id },
    type: "POST"
})
.done(
  (() => {
    removeRowCallback();
  }).bind(this)
)

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

3
Dmitriy 19 Май 2017 в 20:52

Основываясь на хитрости и ответах Зака с типами: Полный пример Привет, мир. Я надеюсь, что это можно только приветствовать, так как это лучший результат в Google при поиске "машинописных обратных вызовов javascript"

type MyCallback = () => string;

class HelloWorld {

    // The callback
    public callback: MyCallback = () => {
        return 'world';
    }

    // The caller
    public caller(callback: MyCallback) {
        alert('Hello ' + callback());
    }
}

let hello = new HelloWorld();
hello.caller(hello.callback);

Это попадает в:

var HelloWorld = (function () {
    function HelloWorld() {
        // The callback
        this.callback = function () {
            return 'world';
        };
    }
    // The caller
    HelloWorld.prototype.caller = function (callback) {
        alert('Hello ' + callback());
    };
    return HelloWorld;
}());
var hello = new HelloWorld();
hello.caller(hello.callback);

Надеюсь, кто-то найдет это немного полезным. :)

0
DarkNeuron 26 Окт 2016 в 18:49

Это своего рода перекрестная публикация из другого ответа (Is есть псевдоним для этого в TypeScript?). Я повторно применил концепцию, используя примеры сверху. Мне это нравится больше, чем в описанных выше вариантах, потому что он явно поддерживает «эту» область видимости как для экземпляра класса, так и для объекта динамического контекста, который вызывает метод.

Ниже представлены две версии. Мне нравится первый, потому что компилятор помогает правильно его использовать (вы не будете так же легко пытаться использовать лямбду обратного вызова , как обратный вызов из-за явно типизированного параметра).

Проверьте это: http://www.typescriptlang.org/Playground/

class Test {

    private testString: string = "Fancy this!";

    // Define the method signature here.
    removeRowLambdaCallback(outerThis: Test): {(): void} {
        alert("Defining callback for consumption");
        return function(){
            alert(outerThis.testString); // lexically scoped class instance
            alert(this); // dynamically scoped context caller
            // Put logic here for removing rows. Can refer to class
            // instance as well as "this" passed by a library such as JQuery or D3.
        }
    }
    // This approach looks nicer, but is more dangerous
    // because someone might use this method itself, rather
    // than the return value, as a callback.
     anotherRemoveRowLambdaCallback(): {(): void} {
        var outerThis = this;
        alert("Defining another callback for consumption");
        return function(){
            alert(outerThis.testString); // lexically scoped class instance
            alert(this); // dynamically scoped context caller
            // Put logic here for removing rows. Can refer to class
            // instance as well as "this" passed by a library such as JQuery or D3.
        }
    }
}

var t = new Test();
var callback1 = t.removeRowLambdaCallback(t);
var callback2 = t.anotherRemoveRowLambdaCallback();

callback1();
callback2();
1
Community 23 Май 2017 в 11:54