在JavaScript里(还有TypeScript),this
关键字的行为与其它语言相比大为不同。这可能会很令人吃惊,特别是对于那些使用其它语言的用户,他们凭借其直觉来想象this
关键字的行为。
这篇文章会教你怎么识别及调试TypeScript里的this
问题,并且提供了一些解决方案和各自的利弊。
丢失this
上下文的典型症状包括:
this.foo
)为undefined
,但其它值没有问题this
的值指向全局的window
对象而不是类实例对象(在非严格模式下)this
的值为undefined
而不是类实例对象(严格模式下)this.doBa()
)失败,错误信息如“TypeError: undefined is not a function”,“Object doesn't support property or method 'doBar'”或“this.doBar is not a function”程序中应该出现了以下代码:
window.addEventListener('click', myClass.doThing);
myPromise.then(myClass.theNextThing);
$(document).ready(myClass.start);
someArray.map(myClass.convert)
<div data-bind="click: myClass.doSomething">
$.ajax(url, { success: myClass.handleData })
this
究竟是什么?已经有大量的文章讲述了JavaScript里this
关键字的危险性。查看这里,这里,或这里。
当JavaScript里的一个函数被调用时,你可以按照下面的顺序来推断出this
指向的是什么(这些规则是按优先级顺序排列的):
function#bind
调用的结果,那么this
指向的是传入bind
的参数foo.func()
形式调用的,那么this
值为foo
this
将为undefined
this
将是全局对象(浏览器环境里为window
)这些规则会产生与直觉相反的效果。比如:
class Foo {
x = 3;
print() {
console.log('x is ' + this.x);
}
}
var f = new Foo();
f.print(); // Prints 'x is 3' as expected
// Use the class method in an object literal
var z = { x: 10, p: f.print };
z.p(); // Prints 'x is 10'
var p = z.p;
p(); // Prints 'x is undefined'
this
的危险信号你要注意的最大的危险信号是在要使用类的方法时没有立即调用它。任何时候你看到类方法被引用了却没有使用相同的表达式来调用时,this
可能已经不对了。
例子:
var x = new MyObject();
x.printThing(); // SAFE, method is invoked where it is referenced
var y = x.printThing; // DANGER, invoking 'y()' may not have correct 'this'
window.addEventListener('click', x.printThing, 10); // DANGER, method is not invoked where it is referenced
window.addEventListener('click', () => x.printThing(), 10); // SAFE, method is invoked in the same expression
可以通过一些方法来保持this
的上下文。
代替TypeScript里默认的原型方法,你可以使用一个实例箭头函数来定义类成员:
class MyClass {
private status = "blah";
public run = () => { // <-- note syntax here
alert(this.status);
}
}
var x = new MyClass();
$(document).ready(x.run); // SAFE, 'run' will always have correct 'this'
this
上下文会比在每次调用时都创建一个闭包来得更有效率一些。this
上下文super
调用基类方法在TypeScrip里(这里为了讲解添加了一些参数) :
var x = new SomeClass();
someCallback((n, m) => x.doSomething(n, m));
var x = new SomeClass();
// SAFE: Functions created from function.bind are always preserve 'this'
window.setTimeout(x.someMethod.bind(x), 100);