在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”程序中應該出現(xiàn)了以下代碼:
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
指向的是什么(這些規(guī)則是按優(yōu)先級順序排列的):
function#bind
調用的結果,那么this
指向的是傳入bind
的參數foo.func()
形式調用的,那么this
值為foo
this
將為undefined
this
將是全局對象(瀏覽器環(huán)境里為window
)這些規(guī)則會產生與直覺相反的效果。比如:
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
上下文會比在每次調用時都創(chuàng)建一個閉包來得更有效率一些。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);