TypeScript里的this

    这篇文章会教你怎么识别及调试TypeScript里的this问题,并且提供了一些解决方案和各自的利弊。

    丢失this上下文的典型症状包括:

    • 类的某字段(this.foo)为undefined,但其它值没有问题
    • this的值指向全局的window对象而不是类实例对象(在非严格模式下)
    • this的值为undefined而不是类实例对象(严格模式下)
    • 调用类方法()失败,错误信息如“TypeError: undefined is not a function”,“Object doesn’t support property or method ‘doBar’”或“this.doBar is not a function”

    程序中应该出现了以下代码:

    • Promise解决,比如myPromise.then(myClass.theNextThing);
    • 第三方库回调,比如$(document).ready(myClass.start);
    • 函数回调,比如someArray.map(myClass.convert)
    • ViewModel类型的库里的类,比如<div data-bind="click: myClass.doSomething">
    • 可选包里的函数,比如$.ajax(url, { success: myClass.handleData })

    当JavaScript里的一个函数被调用时,你可以按照下面的顺序来推断出this指向的是什么(这些规则是按优先级顺序排列的):

    • 如果这个函数是function#bind调用的结果,那么this指向的是传入的参数
    • 如果函数是以foo.func()形式调用的,那么this值为foo
    • 如果是在严格模式下,this将为undefined
    • 否则,this将是全局对象(浏览器环境里为window

    这些规则会产生与直觉相反的效果。比如:

    你要注意的最大的危险信号是在要使用类的方法时没有立即调用它。任何时候你看到类方法被引用了却没有使用相同的表达式来调用时,this可能已经不对了。

    可以通过一些方法来保持this的上下文。

    代替TypeScript里默认的原型方法,你可以使用一个实例箭头函数来定义类成员:

    • 好与坏:这会为每个类实例的每个方法创建额外的闭包。如果这个方法通常是正常调用的,那么这么做有点过了。然而,它经常会在回调函数里调用,让类实例捕获到this上下文会比在每次调用时都创建一个闭包来得更有效率一些。
    • 好:其它外部使用者不可能忘记处理上下文
    • 好:在TypeScript里是类型安全的
    • 坏:派生类不能通过使用super调用基类方法
    • 坏:在类与用户之前产生了额外的非类型安全的约束:明确了哪些方法提前绑定了以及哪些没有

    在TypeScrip里(这里为了讲解添加了一些参数) :

    • 好与坏:内存/效能上的利弊与实例函数相比正相反
    • 好:在TypeScript,100%的类型安全
    • 好:在ECMAScript 3里同样生效
    • 好:你只需要输入一次实例名
    • 坏:你要输出2次参数名
    • 坏:对于可变参数不起作用(’rest’)
    • 好与坏:内存/效能上的利弊与实例函数相比正相反
    • 好:如果函数带参数不需要额外的工作
    • 坏:目前在TypeScript里,不是类型安全的
    • 坏:只在ECMAScript 5里生效