Javascript 如何使用回调函数在 TypeScript 中保留词法范围
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14471975/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can I preserve lexical scope in TypeScript with a callback function
提问by Ralph Lavelle
I have a TypeScript class, with a function that I intend to use as a callback:
我有一个 TypeScript 类,有一个我打算用作回调的函数:
removeRow(_this:MyClass): void {
...
// 'this' is now the window object
// I must use '_this' to get the class itself
...
}
I pass it in to another function
我将它传递给另一个函数
this.deleteRow(this.removeRow);
which in turn calls a jQuery Ajax method, which if successful, invokes the callback like this:
它反过来调用一个 jQuery Ajax 方法,如果成功,它会像这样调用回调:
deleteItem(removeRowCallback: (_this:MyClass) => void ): void {
$.ajax(action, {
data: { "id": id },
type: "POST"
})
.done(() => {
removeRowCallback(this);
})
.fail(() => {
alert("There was an error!");
});
}
The only way I can preserve the 'this' reference to my class is to pass it on to the callback, as demonstrated above. It works, but it's pants code. If I don't wire up the 'this' like this (sorry), then any reference to this in the callback method has reverted to the Window object. Because I'm using arrow functions all the way, I expected that the 'this' would be the class itself, as it is elsewhere in my class.
我可以保留对我的类的“this”引用的唯一方法是将其传递给回调,如上所示。它有效,但它是裤子代码。如果我没有像这样连接“this”(抱歉),那么回调方法中对 this 的任何引用都已恢复到 Window 对象。因为我一直在使用箭头函数,所以我预计“this”将是类本身,因为它在我的类中的其他地方。
Anyone know how to pass callbacks around in TypeScript, preserving lexical scope?
任何人都知道如何在 TypeScript 中传递回调,保留词法范围?
回答by Sly_cardinal
Edit 2014-01-28:
编辑 2014-01-28:
New readers, make sure you check out Zac's answer below.
新读者,请务必查看下方扎克的回答。
He has a much neater solution that will let you define and instantiate a scoped function in the class definition using the fat arrow syntax.
他有一个更简洁的解决方案,可以让您使用粗箭头语法在类定义中定义和实例化作用域函数。
The only thing I will add is that, in regard to option 5in Zac's answer, it's possible to specify the method signature and return type without any repetition using this syntax:
我要补充的唯一一件事是,关于Zac 回答中的选项 5,可以使用以下语法指定方法签名和返回类型而无需任何重复:
public myMethod = (prop1: number): string => {
return 'asdf';
}
Edit 2013-05-28:
编辑 2013-05-28:
The syntax for defining a function property type has changed (since TypeScript version 0.8).
定义函数属性类型的语法已更改(自 TypeScript 0.8 版起)。
Previously you would define a function type like this:
以前,您会像这样定义函数类型:
class Test {
removeRow: (): void;
}
This has now changed to:
现在已更改为:
class Test {
removeRow: () => void;
}
I have updated my answer below to include this new change.
我已经更新了下面的答案以包含此新更改。
As a further aside:If you need to define multiple function signatures for the same function name (e.g. runtime function overloading) then you can use the object map notation (this is used extensively in the jQuery descriptor file):
另外:如果您需要为同一个函数名定义多个函数签名(例如运行时函数重载),那么您可以使用对象映射表示法(这在 jQuery 描述符文件中被广泛使用):
class Test {
removeRow: {
(): void;
(param: string): string;
};
}
You need to define the signature for removeRow()as a property on your class but assign the implementation in the constructor.
您需要将签名定义为removeRow()类上的属性,但在构造函数中分配实现。
There are a few different ways you can do this.
有几种不同的方法可以做到这一点。
Option 1
选项1
class Test {
// Define the method signature here.
removeRow: () => void;
constructor (){
// Implement the method using the fat arrow syntax.
this.removeRow = () => {
// Perform your logic to remove the row.
// Reference `this` as needed.
}
}
}
If you want to keep your constructor minimal then you can just keep the removeRowmethod in the class definition and just assign a proxy function in the constructor:
如果你想让你的构造函数最小化,那么你可以removeRow在类定义中保留该方法,并在构造函数中分配一个代理函数:
Option 2
选项 2
class Test {
// Again, define the method signature here.
removeRowProxy: () => void;
constructor (){
// Assign the method implementation here.
this.removeRowProxy = () => {
this.removeRow.apply(this, arguments);
}
}
removeRow(): void {
// ... removeRow logic here.
}
}
Option 3
选项 3
And finally, if you're using a library like underscore or jQuery then you can just use their utility method to create the proxy:
最后,如果您使用像 underscore 或 jQuery 这样的库,那么您可以使用它们的实用程序方法来创建代理:
class Test {
// Define the method signature here.
removeRowProxy: () => void;
constructor (){
// Use jQuery to bind removeRow to this instance.
this.removeRowProxy = $.proxy(this.removeRow, this);
}
removeRow(): void {
// ... removeRow logic here.
}
}
Then you can tidy up your deleteItemmethod a bit:
然后你可以整理一下你的deleteItem方法:
// Specify `Function` as the callback type.
// NOTE: You can define a specific signature if needed.
deleteItem(removeRowCallback: Function ): void {
$.ajax(action, {
data: { "id": id },
type: "POST"
})
// Pass the callback here.
//
// You don't need the fat arrow syntax here
// because the callback has already been bound
// to the correct scope.
.done(removeRowCallback)
.fail(() => {
alert("There was an error!");
});
}
回答by Zac Morris
UPDATE: See Sly's updated answer. It incorporates an improved version of the options below.
更新:请参阅 Sly 的更新答案。它包含以下选项的改进版本。
ANOTHER UPDATE: Generics
另一个更新:泛型
Sometimes you want to specify a generic type in a function signature without having to specify it on the the whole class. It took me a few tries to figure out the syntax, so I thought it might be worth sharing:
有时您想在函数签名中指定一个泛型类型,而不必在整个类中指定它。我花了一些时间来弄清楚语法,所以我认为它可能值得分享:
class MyClass { //no type parameter necessary here
public myGenericMethod = <T>(someArg:string): QPromise<T> => {
//implementation here...
}
}
Option 4
选项 4
Here are a couple more syntaxes to add to Sly_cardinal's answer. These examples keep the function declaration and implementation in the same place:
这里有一些语法可以添加到 Sly_cardinal 的答案中。这些示例将函数声明和实现保持在同一位置:
class Test {
// Define the method signature AND IMPLEMENTATION here.
public removeRow: () => void = () => {
// Perform your logic to remove the row.
// Reference `this` as needed.
}
constructor (){
}
}
}
or
或者
Option 5
选项 5
A little more compact, but gives up explicit return type (the compiler should infer the return type anyway if not explicit):
更紧凑一点,但放弃显式返回类型(如果不是显式,编译器应该推断返回类型):
class Test {
// Define implementation with implicit signature and correct lexical scope.
public removeRow = () => {
// Perform your logic to remove the row.
// Reference `this` as needed.
}
constructor (){
}
}
}
回答by Dmitriy
Use .bind() to preserve context within the callback.
使用 .bind() 在回调中保留上下文。
Working code example:
工作代码示例:
window.addEventListener(
"resize",
(()=>{this.retrieveDimensionsFromElement();}).bind(this)
)
The code in original question would become something like this:
原始问题中的代码将变成这样:
$.ajax(action, {
data: { "id": id },
type: "POST"
})
.done(
(() => {
removeRowCallback();
}).bind(this)
)
It will set the context (this) inside the callback function to whatever was passed as an argument to bind function, in this case the original this object.
它将回调函数内的上下文(this)设置为作为参数传递给绑定函数的任何内容,在这种情况下是原始的 this 对象。
回答by Eric
This is sort of a cross post from another answer (Is there an alias for 'this' in TypeScript?). I re-applied the concept using the examples from above. I like it better than the options above because it explictly supports "this" scoping to both the class instance as well as the dynamic context entity that calls the method.
这是来自另一个答案的交叉帖子(TypeScript 中是否有“this”的别名?)。我使用上面的例子重新应用了这个概念。我比上面的选项更喜欢它,因为它明确支持类实例以及调用该方法的动态上下文实体的“this”范围。
There are two versions below. I like the first one because the compiler assists in using it correctly (you won't as easily try to misuse the callback lambda itself asthe callback, because of the explicitly typed parameter).
下面有两个版本。我喜欢第一个,因为在正确使用它(你将不容易试图滥用回调拉姆达本身编译助攻的回调,因为显式类型参数)。
Test it out: http://www.typescriptlang.org/Playground/
测试一下: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();
回答by DarkNeuron
Building upon sly and Zac's answers with types: A complete hello world example. I hope this is welcome, seeing as this is the top result in Google, when searching for "typescript javascript callbacks"
基于 sly 和 Zac 对类型的回答:一个完整的 hello world 示例。我希望这是受欢迎的,因为这是谷歌搜索“typescript javascript callbacks”时的最佳结果
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);
This gets transpiled into:
这被转译为:
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);
Hope someone finds it just a little useful. :)
希望有人觉得它有点用。:)

