当在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());