2017-01-15 117 views
0

我讀RxJS4的源代碼和跨越這確實繼承(https://github.com/Reactive-Extensions/RxJS/blob/master/src/core/internal/util.js)函數來了:正確的方式做繼承

var inherits = Rx.internals.inherits = function (child, parent) { 
    function __() { this.constructor = child; } 
    __.prototype = parent.prototype; 
    child.prototype = new __(); 
}; 

我花了一些時間來找出原型鏈,並認爲該功能無法啓用我們做'連接'的孩子和父母,並創建一個繼承的子對象。但我也注意到,如果我創建一個父對象,它的構造函數,它的構造函數將被鏈接到子函數。我是否理解錯誤地使用了這個函數(這是繼承的正確方法,而且我錯誤地鏈接了這些對象)?

enter image description here

+0

無論下面的答案是** **精準,認爲原型鏈爲'單向鏈接list'(EVAL時爲'(讓新的兒童()) - 丙'你從葉去根直到空。你在上面畫的圖是偉大的,除了所有的'constructor'關係是錯誤的。正如代碼明確地說'child.prototype.constructor = child','constructor'是它自己的原型只是一個普通的道具。 – Xlee

+0

是,我不確定是否正確繪製了構造函數鏈接,但我確定它不是'child.prototype.constructor'。它表示'this.constructor = child',這裏'this'是指新對象創建(粉紅色框),所以構造函數鏈接應該從粉紅色框鏈接到孩子fn,或者從綠色框(粉紅色框)到孩子fn,我不能說出這兩個是哪一個正確。@Xlee – kevguy

+0

我知道你的意思,指的是關鍵在js中(這是應用函數的所有者/調用者/上下文)中的'this'的d,顯然在你的情況下,'this'表示'child.prototype'由構造函數創建的新實例(根據你的圖) – Xlee

回答

1

對象和構造

一個你必須在javascript習慣的事情是,構造函數是類。儘管ES6確實引入了class關鍵字和類語法,但它僅僅是底層機制的語法糖。架構沒有改變。

原型是構造函數將用於實例化新對象的構造函數的屬性。所以,例如,如果你想創建大量的「人」,你會寫這樣的東西:

function Person (name) { 
    this.name = name; 
} 

Person.prototype = {}; 

Person.prototype.name = ""; 
Person.prototype.age = null; 
Person.prototype.gender = null; 

請注意一些事情。與其他OO語言相比,這基本上是顛倒的。在其他語言中,您可以定義一個類,然後可以將構造函數定義爲該類的特殊屬性/方法。在JavaScript中,您定義了一個構造函數,然後將該類(原型)定義爲該構造函數的特殊屬性。

二,沒有特別的關鍵字constructor。構造函數只是一個常規函數。沒有什麼特別的。它才成爲一個構造函數,如果你調用使用new關鍵字是:

var x = Person(); // regular function, x is undefined 
var y = new Person(); // constructor, y is an instance of Person 

所以,因爲沒有什麼可以告訴一個程序員的天氣函數是一個構造函數或JavaScript程序員已經開發出一種約定,其中功能的普通函數名稱始終以小寫字母開頭,構造函數名稱始終以大寫字母開頭。從上面的代碼中,您可以看到名爲Person的函數,因此您可以假設我打算將它作爲構造函數。

繼承

因爲原型是,嗯...一個對象的原型,那麼從您孩子的原型設定爲父母的原型實例的構造繼承。在現代的JS,你可以這樣做:

function Employee (name, job) { 
    this.name = name; 
    this.job = job; 
} 

Employee.prototype = Object.create(Person.prototype); // Inherit! 

Employee.prototype.job = null; 

請注意,我們從一個對象(原型)繼承,因爲在JavaScript中你的對象,而不是構造函數,而不是類繼承。

其次,請注意,我們通過將我們的原型設置爲副本來繼承我們父級的原型。這是因爲,如果我們只是將父母的原型分配給我們自己的原型,那麼當我們添加新的屬性時(如本例中的job),我們不想修改父母的原型(因爲那樣不會是繼承)。

在之前的Object.create功能存在的日子裏,你最好別這樣做:

Employee.prototype = new Person(); 

這是今天仍然有效,即使Object.create一般是優選的。所以在RxJS代碼中,當你看到這個時:

child.prototype = new __(); 

這就是繼承發生的地方。請記住,原型是構造函數用作模板來創建新對象的內容。所以,上面的線:

__.prototype = parent.prototype; 

意味着我們現在有一個函數__這將創建一個類似於父母會創建的對象的對象。所以做new __()會創建一個類似於調用父構造函數但不執行父構造函數中定義的任何邏輯的對象。所以基本上它的做法與Object.create(parent)類似;

繼承只是將父級原型的副本分配給我們自己的原型。以上所有其他複雜的比特僅僅是準備複製父母的原型。

+0

我認爲你誤解了我所問的@slebetman。我不問如何做繼承或繼承如何工作,而只是解釋MDN已經做了什麼。相反,我試圖說明爲什麼RxJS使用這個函數繼承而不是使用create()。因爲根據我的理解,任何父對象的.constructor現在都鏈接到子函數。它感覺很奇怪。 – kevguy

+0

@kevlai和'我們會繼承我們的原型設定爲我們的父母prototype'副本是技術上的錯誤在這裏,我們不'copy' /'深copy'什麼,我們只是建立鏈接的數據結構,簡單地說了prop父原型不是我們原型的**自己的**屬性,它們只能從子實例訪問,因爲它們位於層次鏈的更高層。 – Xlee

+0

@Xlee我已經想出了孩子的一部分,我更關心父母的一部分。明確地說,如果我做'新的父()',而不是'新的孩子()',它會是怎麼樣的,因爲正如你所看到的,我已經知道了,如圖所示。 – kevguy

1

但我也注意到如果我創建一個父對象,它的構造函數,它的構造函數將被鏈接到子函數。

這是不正確的。原型鏈是一種方式,父母不以任何方式修改。

這個實現唯一需要注意的是父進程的構造函數在調用子進程的構造函數時不會被調用。 ES6類實現強制執行該操作。

在您的圖表有幾個問題:

  1. __實例沒有一個constructor屬性指向__功能。實際上__存在的原因是將構造函數屬性設置爲子構造函數。

  2. 子實例的__proto__將是__實例(請注意,在繼承代碼new __())和該__proto__parent.prototype

因此,繼承實用注入原型鏈,其唯一目的是確保constructor屬性點來糾正孩子實例類的元對象。

這可能是教育與巴貝爾如何實現ES6類語義進行對比:

function _inherits(subClass, superClass) { 
    if (typeof superClass !== "function" && superClass !== null) { 
     throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); 
    } 
    subClass.prototype = Object.create(superClass && superClass.prototype, { 
     constructor: { 
      value: subClass, 
      enumerable: false, 
      writable: true, 
      configurable: true 
     } 
    }); 
    if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; 
} 

它利用的是Object.create需要可以用來添加額外的屬性第二propertiesObject論點的事實,在這種情況下,恰好是constructor。由Object.create返回的對象與RxJS代碼中new __()返回的__實例具有相同的用途。