在JavaScript里(還有TypeScript),this關(guān)鍵字的行為與其它語言相比大為不同。這可能會(huì)很令人吃驚,特別是對(duì)于那些使用其它語言的用戶,他們憑借其直覺來想象this關(guān)鍵字的行為。
這篇文章會(huì)教你怎么識(shí)別及調(diào)試TypeScript里的this問題,并且提供了一些解決方案和各自的利弊。
丟失this上下文的典型癥狀包括:
this.foo)為undefined,但其它值沒有問題this的值指向全局的window對(duì)象而不是類實(shí)例對(duì)象(在非嚴(yán)格模式下)this的值為undefined而不是類實(shí)例對(duì)象(嚴(yán)格模式下)this.doBa())失敗,錯(cuò)誤信息如“TypeError: undefined is not a function”,“Object doesn't support property or method 'doBar'”或“this.doBar is not a function”程序中應(yīng)該出現(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究竟是什么?已經(jīng)有大量的文章講述了JavaScript里this關(guān)鍵字的危險(xiǎn)性。查看這里,這里,或這里。
當(dāng)JavaScript里的一個(gè)函數(shù)被調(diào)用時(shí),你可以按照下面的順序來推斷出this指向的是什么(這些規(guī)則是按優(yōu)先級(jí)順序排列的):
function#bind調(diào)用的結(jié)果,那么this指向的是傳入bind的參數(shù)foo.func()形式調(diào)用的,那么this值為foothis將為undefinedthis將是全局對(duì)象(瀏覽器環(huán)境里為window)這些規(guī)則會(huì)產(chǎn)生與直覺相反的效果。比如:
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的危險(xiǎn)信號(hào)你要注意的最大的危險(xiǎn)信號(hào)是在要使用類的方法時(shí)沒有立即調(diào)用它。任何時(shí)候你看到類方法被引用了卻沒有使用相同的表達(dá)式來調(diào)用時(shí),this可能已經(jīng)不對(duì)了。
例子:
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里默認(rèn)的原型方法,你可以使用一個(gè)實(shí)例箭頭函數(shù)來定義類成員:
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上下文會(huì)比在每次調(diào)用時(shí)都創(chuàng)建一個(gè)閉包來得更有效率一些。this上下文super調(diào)用基類方法在TypeScrip里(這里為了講解添加了一些參數(shù)) :
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);