衆所周知,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內再進行編碼閉包
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;
}
}
複製代碼
編譯後:ui
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運行的安全性