總所周知,JavaScript語言並非一門面向對象的語言,而是一種解釋性的函數式編程語言。在前端Web還不是很複雜的狀況下,使用JavaScript是能夠應付各類需求的,但當前端頁面變的愈來愈複雜時,JavaScript就顯得比較力不從心了,而TypeScript就是爲了解決這個狀況而誕生的語言。
TypeScript是面向對象的語言同時支持許多面向對象的特性,所以可使用它建立出更增強壯和易於擴展的程序。同時,TypeScript 擴展了 JavaScript 的語法,因此任何現有的 JavaScript 程序能夠不加改變的在 TypeScript 下工做。javascript
根據維基百科的定義:TypeScript是一種由微軟開發的自由和開源的編程語言,它是JavaScript的一個嚴格超集,並添加了可選的靜態類型和基於類的面向對象編程。html
能夠看到,愈來愈多的前端框架開始使用TypeScript,那麼它究竟有哪些優勢呢?下面羅列一些常見的優勢:前端
俗話說,「工欲善其事,必先利其器」,學習一門新的語言和技術必須先了解其開發環境。java
TypeScript提供了兩種主要的方式獲取TypeScript工具:typescript
最新版的Visual Studio 2017和Visual Studio 2015 Update 3默認包含了TypeScript,若是你的Visual Studio還不支持TypeScript,可使用Visual Studio下載頁面連接來獲取安裝插件。同時,針對使用npm的用戶,可使用下面的命令來安裝TypeScript工具。express
npm install -g typescript
除了上面兩種方式外,咱們還可使用TypeScript提供的在線環境來體驗TypeScript的魅力:http://www.typescriptlang.org...。npm
打開編輯器,將下面的代碼輸入到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);
控制函數的執行過程,能夠手動的干預函數執行。示例:
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。
var myArray = [1, 2, 3, 4]; myArray.name = 'daocheng'; myArray.forEach(value => console.log(value)); //結果爲1,2,3,4 //特色:不支持break,會忽略(name)
var myArray = [1, 2, 3, 4]; myArray.name = 'daocheng'; for (var n in myArray ) { console.log(n) } //結果爲1,2,3,4 //特色: 循環的結果是對象或者數組的鍵值。能夠break
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())