当在jquerycallback中调用TypeScript“this”范围问题

我不确定在TypeScript中处理“this”范围的最佳方法。

下面是我转换到TypeScript的代码中的一个常见模式的例子:

class DemonstrateScopingProblems { private status = "blah"; public run() { alert(this.status); } } var thisTest = new DemonstrateScopingProblems(); // works as expected, displays "blah": thisTest.run(); // doesn't work; this is scoped to be the document so this.status is undefined: $(document).ready(thisTest.run); 

现在,我可以改变电话…

 $(document).ready(thisTest.run.bind(thisTest)); 

…哪些工作。 但有点可怕 这意味着在某些情况下代码都可以编译并正常工作,但是如果我们忘记绑定范围,它将会中断。

我想要一种方法在课堂上做,所以当使用这个类时,我们不需要担心“this”的作用范围。

有什么build议么?

更新

另一种可行的方法是使用胖箭头:

 class DemonstrateScopingProblems { private status = "blah"; public run = () => { alert(this.status); } } 

这是一个有效的方法吗?

你有几个select,每个都有自己的权衡。 不幸的是,没有明显的最佳解决scheme,它将取决于应用程序。

自动类绑定
如你的问题所示:

 class DemonstrateScopingProblems { private status = "blah"; public run = () => { alert(this.status); } } 
  • 好/坏:这会为每个类的实例创build每个方法的附加闭包。 如果此方法通常只用于常规的方法调用,这是矫枉过正的。 但是,如果在callback位置中使用了很多,那么类实例捕获this上下文会更有效率,而不是每个调用站点在调用时创build一个新的闭包。
  • 好:外部呼叫者不可能忘记处理this情况
  • 好的:TypeScript中的Typesafe
  • 好:如果函数有参数,不需要额外的工作
  • 坏:派生类不能调用使用super.这种方式写的基类方法super.
  • 不好的:确切的语义是哪些方法是“预先约束的”,哪些不会在你的类和消费者之间创build一个额外的非types安全的契约。

Function.bind
还如图所示:

 $(document).ready(thisTest.run.bind(thisTest)); 
  • 好/坏:与第一种方法相比,存储/性能相反
  • 好:如果函数有参数,不需要额外的工作
  • 不好:在TypeScript中,这当前没有types安全
  • 错误:只有在ECMAScript 5中可用,如果这对您很重要
  • 不好:你必须input两次实例名称

脂肪箭头
在TypeScript中(出于解释的原因在这里显示一些虚拟参数):

 $(document).ready((n, m) => thisTest.run(n, m)); 
  • 好/坏:与第一种方法相比,存储/性能相反
  • 好:在TypeScript中,这有100%的types安全
  • 好:在ECMAScript 3中有效
  • 好:你只需input一次实例名称
  • 不好:你必须input两次参数
  • 错误:不能使用可变参数

另一个解决scheme需要一些初始设置,但它的无敌轻,字面上单字的语法是使用方法装饰器通过getter JIT绑定方法。

我已经在GitHub上创build了一个回购站来展示这个想法的实现(对于包含注释的40行代码来说,这个想法有点冗长) ,您可以简单地使用它:

 class DemonstrateScopingProblems { private status = "blah"; @bound public run() { alert(this.status); } } 

我还没有看到这个提到,但它完美的作品。 另外,这种方法没有明显的缺点:这个装饰器的实现( 包括对运行时types安全的一些types检查)是简单而直接的,并且在初始方法调用之后基本上没有开销。

基本部分是在类原型上定义下面的getter,它第一次调用之前立即执行:

 get: function () { // Create bound override on object instance. This will hide the original method on the prototype, and instead yield a bound version from the // instance itself. The original method will no longer be accessible. Inside a getter, 'this' will refer to the instance. var instance = this; Object.defineProperty(instance, propKey.toString(), { value: function () { // This is effectively a lightweight bind() that skips many (here unnecessary) checks found in native implementations. return originalMethod.apply(instance, arguments); } }); // The first invocation (per instance) will return the bound method from here. Subsequent calls will never reach this point, due to the way // JavaScript runtimes look up properties on objects; the bound method, defined on the instance, will effectively hide it. return instance[propKey]; } 

完整的来源


这个想法也可以更进一步,通过在一个类装饰器中做这个,迭代遍历方法并在一个遍中为每个方法定义上面的属性描述符。

Necromancing。
有一个明显的简单的解决scheme,不需要箭头函数(箭头函数慢30%),或者通过getter的JIT方法。
该解决scheme是在构造函数中绑定这个上下文。

 class DemonstrateScopingProblems { constructor() { this.run = this.run.bind(this); } private status = "blah"; public run() { alert(this.status); } } 

在你的代码中,你是否尝试过更改最后一行,如下所示?

 $(document).ready(() => thisTest.run());