#TypeScript簡介
定義
總所周知,JavaScript語言並不是一門物件導向的語言,而是一種解釋性的函數語言程式設計語言。在前端Web還不是很複雜的情況下,使用JavaScript是可以應付各種需求的,但當前端頁面變的越來越複雜時,JavaScript就顯得比較力不從心了,而TypeScript就是為了解決這個情況而誕生的語言。 TypeScript是物件導向的語言同時支援許多物件導向的特性,因此可以使用它建立出更加強壯和易於擴充套件的程式。同時,TypeScript 擴充套件了 JavaScript 的語法,所以任何現有的 JavaScript 程式可以不加改變的在 TypeScript 下工作。
根據維基百科的定義:TypeScript是一種由微軟開發的自由和開源的程式語言,它是JavaScript的一個嚴格超集,並新增了可選的靜態型別和基於類的物件導向程式設計。
TypeScript的優勢
可以看到,越來越多的前端框架開始使用TypeScript,那麼它究竟有哪些優點呢?下面羅列一些常見的優點:
- 更多的規則和型別限制,讓程式碼預測性更高,可控性更高,易於維護和除錯。
- 對模組、名稱空間和麵向物件的支援,更容易組織程式碼開發大型複雜程式。
- TypeScript 的編譯步驟可以捕獲執行之前的錯誤。
- Angular2+ 和 Ionic2+ 預設使用TypeScript,同時Vue.js和React.js等一些流行的前端框架也開始支援TypeScript。
- ...
環境搭建
俗話說,“工欲善其事,必先利其器”,學習一門新的語言和技術必須先了解其開發環境。
安裝TypeScript
TypeScript提供了兩種主要的方式獲取TypeScript工具:
- 通過npm(Node.js包管理器)
- 安裝Visual Studio的TypeScript外掛
最新版的Visual Studio 2017和Visual Studio 2015 Update 3預設包含了TypeScript,如果你的Visual Studio還不支援TypeScript,可以使用Visual Studio下載頁面連結來獲取安裝外掛。同時,針對使用npm的使用者,可以使用下面的命令來安裝TypeScript工具。
npm install -g typescript
複製程式碼
除了上面兩種方式外,我們還可以使用TypeScript提供的線上環境來體驗TypeScript的魅力:www.typescriptlang.org/play/index.…
建立TypeScript檔案
開啟編輯器,將下面的程式碼輸入到greeter.ts檔案裡。
function greeter(person) {
return "Hello, " + person;
}
let user = "jack ma";
document.body.innerHTML = greeter(user);
複製程式碼
編譯程式碼
TypeScript使用.ts作為副檔名,但是這段程式碼僅僅是JavaScript而已,想要執行這段程式碼,還需要編譯上面的程式碼。在命令列上,執行TypeScript編譯器:
tsc greeter.ts
複製程式碼
輸出結果為一個greeter.js檔案,它包含了和輸入檔案中相同的JavsScript程式碼。此時,我們就可以執行這段程式碼了。
型別註解
TypeScript裡的型別註解是一種輕量級的為函式或變數新增約束的方式。 在這個例子裡,我們希望 greeter函式接收一個字串引數,那麼我們可以這麼做:
function greeter(person: string) {
return "Hello, " + person;
}
let user = [0, 1, 2];
document.body.innerHTML = greeter(user);
複製程式碼
重新編譯,會看到一個錯誤:
greeter.ts(7,26): error TS2345: Argument of type 'number[]' is not assignable to parameter of type 'string'.
複製程式碼
介面
讓我們開發這樣一個示例:使用一個介面,它描述了具有firstName和lastName欄位的物件。在TypeScript中,如果兩個型別其內部結構相容,那麼這兩種型別相容。這使我們實現一個介面,僅僅只需必要的結構形狀,而不必有明確的implements子句。
interface Person {
firstName: string;
lastName: string;
}
function greeter(person: Person) {
return "Hello, " + person.firstName + " " + person.lastName;
}
let user = { firstName: "Jane", lastName: "User" };
document.body.innerHTML = greeter(user);
複製程式碼
類
TypeScript支援JavaScript的新特性,比如支援基於類的物件導向程式設計。讓我們建立一個Student類,它帶有一個建構函式和一些公共欄位。
class Student {
fullName: string;
constructor(public firstName, public middleInitial, public lastName) {
this.fullName = firstName + " " + middleInitial + " " + lastName;
}
}
interface Person {
firstName: string;
lastName: string;
}
function greeter(person : Person) {
return "Hello, " + person.firstName + " " + person.lastName;
}
let user = new Student("Jane", "M.", "User");
document.body.innerHTML = greeter(user);
複製程式碼
重新執行tsc greeter.ts,你會看到生成的JavaScript程式碼和原先的一樣。 TypeScript裡的類只是JavaScript裡常用的基於原型物件導向程式設計的簡寫。
語法特性
和JavaScript相比,TypeScript帶來了諸多語法上的變化,下面就其比較重要的羅列如下。
字串特性
多行字串
使用``包裹跨行的字串,示例:
var html = `<div>
<span></span>
</div>`
複製程式碼
字串模板
可以在多行字串中使用模板,示例:
var names = 'daocheng';
function getImg() {
return '<i></i>'
}
var html = `<div>${names}
<span>${getImg()}</span>
<div>
`
複製程式碼
###自動拆分字串
function getData(template, name, age) {
console.log(template);
console.log(name);
console.log(age);
}
var names = 'daocheng';
var age = 23;
getData`你好,我的名字是${names},我今年${age}歲了`
複製程式碼
引數
引數型別
Typescript中的引數型別包括: boolean/number/string/array/tuple/enum/any/(null和undefined)/ void /never。 其中元祖(tuple)、列舉、任意值、void型別和never是有別於Javascript的特有型別。
型別宣告與預設引數
在Typescritpt中宣告變數,需要加上型別宣告,如boolean或string等。通過靜態型別約束,在編譯時執行型別檢查,這樣可以避免一些型別混用的低階錯誤。示例:
var names = 'daocheng';
function getData(name: stirng, age: number): boolean {
return true
}
複製程式碼
Typescript還支援初始化預設引數。如果函式的某個引數設定了預設值,當該函式被呼叫時,如果沒有給這個引數傳值或者傳值為undefined時,這個引數的值就是設定的預設值。示例:
function max(x: number, y: number = 4): number {
return x > y ? x : y;
}
let result1 = max(2); //正常
let result2 = max(2, undefined); //正常
let result3 = max(2, 4, 7); //報錯
let result4 = max(2, 7); //正常
複製程式碼
可選引數
在javascript裡,被呼叫函式的每個函式都是可選的,而在typescript中,被呼叫的每個函式的每個引數都是必傳的。在編譯時,會檢查函式每個引數是否傳值。簡而言之,傳遞給一個函式的引數個數必須和函式定義的引數個數一致。例如:
function max(x: number, y: number) {
if(y){
return x > y ? x : y;
} else {
return x
}
}
let result1 = max(2);
let result2 = max(2, 4, 7); //報錯
let result3 = max(2, 4);
//注意:可選引數必須放在預設引數後面
複製程式碼
函式
剩餘函式
當需要同時操作多個引數,或者並不知道會有多少引數傳遞進來時,就需要用到Typescript 裡的剩餘引數。示例:
function sum(x: number, ...restOfNumber: number[]){
let result = x;
restOfNumber.forEach(value => result += value);
return result;
}
let result1 = sum(1, 2, 3, 4, 5, 6);
console.log(result1);
let result2 = sum(2);
console.log(result2);
let result3 = sum(2, 5);
console.log(result3);
複製程式碼
generator函式
控制函式的執行過程,可以手動的干預函式執行。示例:
function getPrice(stock) {
while (1) {
yield Math.random() * 100;
}
}
var priceGenerator = getPrice('dcc');
var limitPrice = 51;
var price = 100;
while (price > limitPrice) {
price = priceGenerator.next().value;
console.log(`this generator return ${price}`);
}
console.log(`buying at ${price}`);
複製程式碼
析構表示式
析構表示式又稱解構,是ES6的一個重要特性,Typescript在1.5版本中開始增加了對結構的支援,所謂結構,就是將宣告的一組變數與相同結構的陣列或者物件的元素數值一一對應。分陣列解構([])和物件解構({})兩種。
陣列解構
let imput = [1, 2];
let [first, second] = input;
console.log(first); //相當於inputp[0]
console.log(second); //相當於input[1]
function f([first, second]) {
console.log(first + second)
}
f{[1, 3]} //結果是4
let [first, ...rest] = [1, 2, 3, 4];
console.log(first); //1
console.log(second); //[2,3,4]
複製程式碼
物件解構
let test = {
x: 0,
y: 0,
width: 15,
heights: {
height1: 10,
height2: 20
}
};
let { x, y: myY, width, heights: {height2} } = test;
console.log(x, myY, width, height2); //輸出:0,10,15,20
複製程式碼
箭頭表示式
用來宣告匿名函式,消除傳統匿名函式的this指標問題。例如:
function Test1(names: string) {
this.names = names;
setInterval(function() {
console.log('my name is ' + this.names);
}, 1000)
}
function Test2(names: string) {
this.names = names;
setInterval(() => {
console.log('my names is ' + this.names)
}, 1000)
}
var a = new Test1('daocheng'); //undefined
var b = new Test2('daocheng'); //daocheng
複製程式碼
迴圈
typescritpt中涉及三種高階迴圈方式:forEach()、for in、for of。
forEach
var myArray = [1, 2, 3, 4];
myArray.name = 'daocheng';
myArray.forEach(value => console.log(value)); //結果為1,2,3,4
//特點:不支援break,會忽略(name)
複製程式碼
for in
var myArray = [1, 2, 3, 4];
myArray.name = 'daocheng';
for (var n in myArray ) {
console.log(n)
} //結果為1,2,3,4
//特點: 迴圈的結果是物件或者陣列的鍵值。可以break
複製程式碼
for of
var myArray = [1, 2, 3, 4];
myArray.name = 'daocheng';
for (var n of myArray) {
console.log(n)
} //結果是1,2,3,4
//特點:忽略屬性,可以打斷。當迴圈為字串時,會把字串中每個字元打出來
複製程式碼
類
傳統的JavaScript程式使用函式和基於原型(Prototype)繼承來建立可重用的“類”,這對於習慣了物件導向程式設計的開發者來說不是很友好,Typescript中可以支援基於類(class)的物件導向程式設計。
類的宣告
class Car {
engine: string,
constructor(engine: string) {
this.engine = engine;
}
drive(distanceInMeters: number = 0) {
console.log(`aaa is running` + this.engine)
}
}
let car = new Car('petrol');
car.drive(100)
複製程式碼
類的封裝、繼承、多型
封裝、繼承、多型是物件導向的三大特性。上面的例子把汽車的行為寫到一個類中,即所謂的封裝。在Typescript中,使用extends關鍵字可以方便的實現。例如:
繼承
繼承就是類與類之間一種特殊與一般的關係,可以理解成“is a”的關係。在繼承關係中,子類可以無條件的繼承父類的方法和屬性。
class Car {
engine: string;
constructor(engine: string) {
this.engine = engine;
}
drive(distanceInMeter: number = 0){
console.log(`A car runs ${distanceInMeter}m
powered by` + this.engine)
}
}
class MotoCar extends Car {
constructor(engine: string) {
super(engine)
}
}
let tesla = new MotoCar('electricity');
tesla.drive();
//其中子類MotoCar的例項物件tesla呼叫了父類Car的drive()方法。
複製程式碼
多型
多型就是通過對傳遞的引數判斷來執行邏輯,即可實現一種多型處理機制。
class Car {
engine: string;
constructor(engine: string) {
this.engine = engine;
}
drive(distanceInMeter: number = 0){
console.log(`A car runs ${distanceInMeter}m
powered by` + this.engine)
}
}
class Jeep extends Car {
constructor(engine: string) {
super(engine)
}
drive(distanceInMeters: number = 100) {
console.log('jeep...')
return super.drive(distanceInMeters);
}
}
let landRover: Car = new Jeep('petrol'); //實現多型
複製程式碼
Jeep子類中的drive()方法重寫了Car的drive()方法,這樣drive()方法在不同的類中就具有不同的功能,這就是多型。注意:子類和派生類的建構函式中必須呼叫super(),它會實現父類構造方法。
引數屬性
引數屬性是通過給建構函式的引數新增一個訪問限定符來宣告。引數屬性可以方便地讓我們在一個地方定義並初始化類成員。
class Car {
constructor(public engine: string) {}
drive() { }
}
複製程式碼
抽象類
Typescript有抽象類的概念,它是供其他類繼承的基類,不能直接被例項化。不同於介面,抽象類必須包含一些抽象方法,同時也可以包含非抽象的成員。抽象類中的抽象方法必須在派生類中實現。
abstract class Person {
abstract speak(): void;
walking(): void {
console.log('walking');
}
}
class Male extends Person {
speak(): void {
console.log('man wakling')
}
}
複製程式碼
介面
介面在物件導向設計中具有極其重要的作用,在Gof的23種設計模式中,基本上都可見到介面的身影。長期以來,介面模式一直是Javascript這類弱型別語言的軟肋,Typescript介面的使用方式類似於Java。 在Typescript中介面有屬性型別、函式型別、可索引型別、類型別這幾種,在Angular的開發中主要使用類型別介面,我們使用interface關鍵字定義介面並用implements關鍵字實現介面。
interfance Animal {
name: string;
setName();
}
class Dog implements Animal {
name: string;
setName() {
console.log(this.name)
}
constructor() { }
}
//介面更注重功能的設計,抽象類更注重結構內容的體現
複製程式碼
模組
ES6中引入了模組的概念,在TypeScript中也支援模組的使用。使用import和export關鍵字來建立兩個模組之間的聯絡。
##裝飾器 裝飾器(Decorators)是一種特殊型別的宣告,它可以被附加到類宣告、方法、屬性或引數上。裝飾器有@符號緊接一個函式名稱,如:@expression,expression求職後必須是一個函式,在函式執行的時候裝飾器的宣告方法會被執行。裝飾器是用來給附著的主題進行裝飾,新增額外的行為。(裝飾器屬於ES7規範)
在Typescript的原始碼中,官方提供了方法裝飾器、類裝飾器、引數裝飾器、屬性裝飾器等幾種每種裝飾器型別傳入的引數大不相同。這裡我演示兩種裝飾器。例如:
function Component(component) {
console.log('selector: ' + component.selector);
console.log('template: ' + component.template);
console.log('component init');
return (target: any) => {
console.log('component call');
return target;
}
}
function Directive() {
console.log('directive init');
return (target: any) => {
console.log('directive call');
return target;
}
}
@Component({
selector: 'person',
template: 'person.html'
})
@Directive()
export class Person {}
let p = new Person();
複製程式碼
C#的首席架構師以及Delphi和Turbo Pascal的創始人安德斯•海爾斯伯格參與了TypeScript的開發。Typescript是ES6的超集。新增了可選的靜態型別(注意並不是強型別)和基於類的物件導向程式設計。(如果對ES6熟悉那麼可以只關注類、裝飾器部分的內容。)
泛型
泛型是引數化的型別,一般用來限制集合的內容。例如:
class MinHeap<T> {
list: T[] = [];
add(element: T): void {
//這裡進行大小比較,並將最小值放在陣列頭部,功能程式碼省略。
}
min(): T {
return this.list.length ? this.list[0] : null
}
}
let heap = new MinHeap<number>();
heap.add(3);
heap.add(5);
console.log(heap.min())
複製程式碼