鍍金池/ 教程/ HTML/ TypeScript里的this
初始化項目結構
聯(lián)合類型
介紹
介紹
介紹
編譯選項
TypeScript 1.6
介紹
介紹
發(fā)展路線圖
介紹
在MSBuild里使用編譯選項
可迭代性
TypeScript 1.3
介紹
介紹
TypeScript 1.1
變量聲明
即將到來的Angular 2框架是使用TypeScript開發(fā)的。 因此Angular和TypeScript一起使用非常簡單方便
tsconfig.json
介紹
介紹
介紹
在MSBuild里使用編譯選項
使用TypeScript的每日構建版本
新建工程
枚舉
三斜線指令
結合ASP.NET v5使用TypeScript
TypeScript里的this
介紹
TypeScript 1.4
編碼規(guī)范
介紹
模塊解析
ASP.NET 4
架構概述
介紹
介紹
ASP.NET Core
TypeScript 1.8
介紹
介紹
創(chuàng)建簡單工程
TypeScript 1.7
TypeScript 1.5
NPM包的類型
支持TypeScript的編輯器

TypeScript里的this

介紹

在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)了以下代碼:

  • 事件監(jiān)聽,比如window.addEventListener('click', myClass.doThing);
  • 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究竟是什么?

已經有大量的文章講述了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'
  • 好與壞:這會為每個類實例的每個方法創(chuàng)建額外的閉包。如果這個方法通常是正常調用的,那么這么做有點過了。然而,它經常會在回調函數里調用,讓類實例捕獲到this上下文會比在每次調用時都創(chuàng)建一個閉包來得更有效率一些。
  • 好:其它外部使用者不可能忘記處理this上下文
  • 好:在TypeScript里是類型安全的
  • 好:如果函數帶參數不需要額外的工作
  • 壞:派生類不能通過使用super調用基類方法
  • 壞:在類與用戶之前產生了額外的非類型安全的約束:明確了哪些方法提前綁定了以及哪些沒有

本地的胖箭頭

在TypeScrip里(這里為了講解添加了一些參數) :

var x = new SomeClass();
someCallback((n, m) => x.doSomething(n, m));
  • 好與壞:內存/效能上的利弊與實例函數相比正相反
  • 好:在TypeScript,100%的類型安全
  • 好:在ECMAScript 3里同樣生效
  • 好:你只需要輸入一次實例名
  • 壞:你要輸出2次參數名
  • 壞:對于可變參數不起作用('rest')

Function.bind

var x = new SomeClass();
// SAFE: Functions created from function.bind are always preserve 'this'
window.setTimeout(x.someMethod.bind(x), 100);
  • 好與壞:內存/效能上的利弊與實例函數相比正相反
  • 好:如果函數帶參數不需要額外的工作
  • 壞:目前在TypeScript里,不是類型安全的
  • 壞:只在ECMAScript 5里生效
  • 壞:你要輸入2次實例名
上一篇:TypeScript 1.4下一篇:NPM包的類型