TypeScript中的原型繼承

Point3D繼承Point

【注:如下TypeScript簡稱爲TS】javascript

咱們從一段實現類繼承的代碼開始(出自TS):java

var Point3D = (function (_super) {
    __extends(Point3D, _super);
    function Point3D(x, y, z) {
        _super.call(this, x, y);
        this.z = z;
    } P
    Point3D.prototype.add = function (point) {
        var point2D = _super.prototype.add.call(this, point);
        return new Point3D(point2D.x, point2D.y, this.z + point.z);
    };
    return Point3D;
})(Point);
複製代碼

這種被叫作當即調用函數表達式(IIFE [Immediately-Invoked Function Expression])git

  • 在這裏_super也就是基類Point。

__extends實現以下(這段代碼也是TS裏實現類繼承的簡化版本):typescript

var __extends = this.__extends || function (Child, Parent) {
    for (var p in Parent) if (Parent.hasOwnProperty(p)) Child[p] = Parent[p];
    function __() { this.constructor = Child; }
    __.prototype = Parent.prototype;
    Child.prototype = new __();
};
複製代碼
  • Child是子類,Parent是父類
  • Child,Parent都只是函數,並非實例,new出來的纔是實例。
  • 把父類靜態屬性傳給子類靜態屬性。for (var p in Parent) if (Parent.hasOwnProperty(p)) Child[p] = Parent[p];這裏的靜態屬性是指直接用hasOwnProperty可以訪問到的屬性,而並不是原型鏈上的屬性。
  • 接下去的3行代碼,主要目的是把子類的函數原型與父類的函數原型對接上,用一句代碼表示的話就是Child.prototype.__proto__ = Parent.prototype,而後爲啥一句話就能搞定,可是TS要寫3句話,那是由於__proto__這個屬性有兼容性問題,有些瀏覽器叫法不一樣,或者有些根本不讓你去訪問到,但確確實實在其內部存在這麼一個東西,接下去咱們先來把Child.prototype.__proto__ = Parent.prototype這個解釋通,而後再來講下剩下的3行代碼

Child.prototype.proto = Parent.prototype

首先先來名詞解釋一下瀏覽器

1 __proto__

在JS裏,全部實例對象都有一個__proto__(拋開瀏覽器兼容性,只說概念),做用是:若是訪問某個對象的屬性obj.abc沒有的話,那就會去obj.__proto__.abc去找,若是仍是沒有找到,那就去obj.__proto__.__proto__.abc找,以此類推,一直到__proto__爲null結束,若是還沒找到,那就真的沒有了。這個就是js支持的原型繼承。舉例:函數

var foo = {}
// setup on foo as well as foo.__proto__
foo.bar = 123;
foo.__proto__.bar = 456;
console.log(foo.bar); // 123
delete foo.bar; // remove from object
console.log(foo.bar); // 456
delete foo.__proto__.bar; // remove from foo.__proto__
console.log(foo.bar); // undefined
複製代碼

2 prototype

__proto__是給實例用的,而prototype是給函數用的,每一個JS裏的function都有個prototype(這個貌似沒有兼容性問題,比較統一),而後prototype有一個成員叫constructor,這個constructor又反過來指向函數本身。代碼以下:ui

function Foo() { }
console.log(Foo.prototype); // {} i.e. it exists and is not undefined
console.log(Foo.prototype.constructor === Foo); // Has a member called `constructor` pointing
複製代碼

3 用了new調用構造函數以後,函數裏的this作了些啥

this在函數裏指向了當前這個被新建立出來的對象實例,通常咱們會在構造函數裏用this來改一下這個實例的初始值等。this

function Foo() {
    this.bar = 123;
} 

// call with the new operator
var newFoo = new Foo();
console.log(newFoo.bar); // 123
複製代碼

我以爲能夠這麼認爲,用了new關鍵字來調用函數後,不但建立了一個新對象,並且在函數最後強制return了裏面的this,從而可以賦值給左邊那個變量(newFoo)spa

4 用了new調用構造函數以後,prototype__proto__作了些啥

調用了new以後,會把這個函數的prototype賦值給這個新對象實例的__proto__屬性。舉例:prototype

function Foo() { }
var foo = new Foo();
console.log(foo.__proto__ === Foo.prototype); // True!
複製代碼

再來看看__extends剩下的3行

1 function __() { this.constructor = Child; }
2 __.prototype = Parent.prototype;
3 Child.prototype = new __();
複製代碼

咱們倒過來看,

第3行 Child.prototype = new __() 意思實際上是: Child.prototype = {__proto__ : __.prototype} (上面第4點)

而後結合第2行代碼 __.prototype = Parent.prototype , 其實就變成了 Child.prototype = {__proto__ : Parent.prototype}

到這裏,其實咱們已經完成目標Child.prototype.__proto__ = Parent.prototype

而後爲了和普通函數擁有相同的定義,參考第1點,咱們須要再復原一下Child.prototype.constructor,這也就是第一行function __() {this.constructor = Child; }作的事情,最後就變成了Child.prototype = {__proto__ : Parent.prototype , constructor : Child} (上面第2點)

而後疑問來了,咱們爲啥要復原這個constructor,其實沒有這個constructor,咱們大部分繼承功能都能使用,具體能夠參考一下這裏

大概理解爲,這個玩意只有在你本身手動調用的時候纔有用,好比有一個var ball = new Football();,你用着用着,發現忘了這個ball是Football仍是Basketball構造出來的,你確實又想要一個和ball同類型的,那你能夠這樣來 var ball2 = ball.constructor();,這樣ball2和ball就同樣了,不過好像用不太到就是。。。若是有小夥伴知道更多的理解,麻煩評論給我哈。。。。

這篇文章是看了TypeScript Deep Dive關於TS類轉成JS實現那部分以後總結的,若有不對的地方還但願你們評論給我。

相關文章
相關標籤/搜索