衆所周知,ts目前是不能直接編譯成機器碼運行的
須要轉換成js代碼後運行
因爲js的動態特性,因此我也很好奇編譯後的代碼是怎麼樣的呢javascript
廢話很少說,直接切入正題
注:(ts版本爲:3.1,編譯的js版本爲:ES5)java
首先寫一個基類typescript
class Person { public name: string; private _age: number; protected sex: string; constructor(name: string, age: number, sex: string) { this.name = name; this.sex = sex; this._age = age; } public say():void { console.log(`my name is ${this.name}`) } } 複製代碼
編譯後:安全
var Person = /** @class */ (function () { function Person(name, age, sex) { this.name = name; this.sex = sex; this._age = age; } Person.prototype.say = function () { console.log("my name is " + this.name); }; return Person; }()); 複製代碼
能夠看到編譯後的代碼還少了不少
我本覺得編譯後的代碼應該更復雜些,
好比用閉包實現private等等
後來一想,真不必,ts是js的超集
只要保證在ts內,代碼的安全性能獲得保證就行
畢竟,咱們不會在編譯後的js內再進行編碼markdown
let p: Person = new Person('小明', 20, '男'); // error:屬性「_age」爲私有屬性,只能在類「Person」中訪問。ts(2341) p._age = 21; 複製代碼
注意,構造函數也能夠進行描述,private後將不能實例化對象,protected後只能由繼承的類調用其構造函數 閉包
咱們再來看看其餘修飾器編譯以後的代碼函數
class Person { public readonly age: number; private _height: number; get height(): number { return this._height; } constructor( age: number, height: number) { this.age = age; this._height = height; } } 複製代碼
編譯後:性能
var Person = /** @class */ (function () { function Person(age, height) { this.age = age; this._height = height; } Object.defineProperty(Person.prototype, "height", { get: function () { return this._height; }, enumerable: true, configurable: true }); return Person; }()); 複製代碼
這裏只有get ts能推斷出屬性是隻讀
咱們代碼主動設置屬性readonly也是隻讀
但要注意他們編譯出來的代碼不一樣this
let p: Person = new Person(20, 170); // error:Cannot assign to 'age' because it is a read-only property.ts(2540) p.age = 22; // error:Cannot assign to 'height' because it is a read-only property.ts(2540) p.height = 180; 複製代碼
下面咱們來寫一個子類編碼
class Person { public name: string; private _age: number; protected sex: string; constructor(name: string, age: number, sex: string) { this.name = name; this.sex = sex; this._age = age; } public say() { console.log(`my name is ${this.name}`) } } class Boy extends Person { constructor(name: string, age: number) { super(name, age, '男'); } play() { } } 複製代碼
編譯後:
"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); Object.defineProperty(exports, "__esModule", { value: true }); var Person = /** @class */ (function () { function Person(name, age, sex) { this.name = name; this.sex = sex; this._age = age; } Person.prototype.say = function () { console.log("my name is " + this.name); }; return Person; }()); var Boy = /** @class */ (function (_super) { __extends(Boy, _super); function Boy(name, age) { return _super.call(this, name, age, '男') || this; } Boy.prototype.play = function () { }; return Boy; }(Person)); 複製代碼
能夠看到編譯成ES5的js代碼class的實現仍是特別規範的
使用的組合寄生繼承方實現式,
沒有變量提高,靜態屬性也繼承了
要注意ts和別的強類型語言不一樣點
類既能被繼承,也能被當作接口去實現
而接口,只能去實現
如:
class Person { public name: string; constructor(name: string) { this.name = name; } public say() { console.log(`my name is ${this.name}`) } } interface IStudy { read(): void; write(): void; } class Boy extends Person implements IStudy { constructor(name: string) { super(name); } read(): void { console.log('boy read'); } write(): void { } } class Girl implements Person, IStudy { public name: string; constructor(name: string) { this.name = name; } read(): void { console.log('girl read'); } write(): void { } say(): void { } } 複製代碼
Boy是繼承自Person
而Girl是實現了Person
編譯代碼爲:
var Person = /** @class */ (function () { function Person(name) { this.name = name; } Person.prototype.say = function () { console.log("my name is " + this.name); }; return Person; }()); exports.Person = Person; var Boy = /** @class */ (function (_super) { __extends(Boy, _super); function Boy(name) { return _super.call(this, name) || this; } Boy.prototype.read = function () { console.log('boy read'); }; Boy.prototype.write = function () { }; return Boy; }(Person)); var Girl = /** @class */ (function () { function Girl(name) { this.name = name; } Girl.prototype.read = function () { console.log('girl read'); }; Girl.prototype.write = function () { }; Girl.prototype.say = function () { }; return Girl; }()); 複製代碼
這裏編譯後的js代碼也直觀的顯示了繼承和實現的區別
Boy繼承自Person,因此原型鏈上能找到父類的方法屬性
Girl實現了Person,因此只是自身擁有和Person同樣結構的屬性和方法
let s1: Boy = new Boy('小明'); let s2: Girl = new Girl('小紅'); console.log(s1 instanceof Person); // true console.log(s2 instanceof Person); // false 複製代碼
相較其餘強類型語言,更有意思的是
ts中的類型兼容,更像是填鴨大法
只要兩個類型擁有相同的結構,就能夠兼容
好比接着上面的代碼:
let boy: Boy = new Boy('小明'); let girl: Girl = new Girl('小紅'); boy = girl;// ok let tmp: Girl = new Boy('小黑'); // ok 複製代碼
甚至還能夠:
function factory(): IStudy & Person { // return new Boy('小綠'); // ok return new Girl('小紫'); // ok } 複製代碼
因爲js中弱類型的特性,普遍的使用了匿名對象,動態屬性等等
因此ts中對類型的兼容的斷定都是基於"結構",而不是它的名義類型
多態是弱類型語言與生俱來的,或者說不該該有這個概念
而ts中也是與生俱來
let s1: IStudy = new Boy('小明'); let s2: IStudy = new Girl('小紅'); s1.read();// boy read s2.read();// girl read 複製代碼
由於多態的定義原本就是:
同一操做做用於不一樣的對象,能夠有不一樣的解釋,產生不一樣的執行結果。
因此重寫,實現,或是重載都能表現
js原本就有面向對象的實現,ts只是更好的表現出來 ts對類型的兼容是基於「結構」的兼容 因此ts中的"類",既能被繼承(extends),也能被實現(implements) ts是js的超集,不少特徵在編譯後的js代碼內並不能體現,可是能保證編譯後js運行的安全性