從編譯後的js代碼看typescript中的面向對象

衆所周知,ts目前是不能直接編譯成機器碼運行的
須要轉換成js代碼後運行
因爲js的動態特性,因此我也很好奇編譯後的代碼是怎麼樣的呢javascript

廢話很少說,直接切入正題
注:(ts版本爲:3.1,編譯的js版本爲:ES5)java

封裝

public/private/protected

首先寫一個基類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的實現仍是特別規範的
使用的組合寄生繼承方實現式,
沒有變量提高,靜態屬性也繼承了

extend與implements

要注意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運行的安全性

相關文章
相關標籤/搜索