JavaScript痛點系列(六):原型鏈與繼承

來源:互聯網
上載者:User

標籤:ret   引用   傳遞參數   color   實現   this   參數   console   red   

類和建構函式

JS中使用建構函式來定義類:

function Range(from, to) {    this.from = from    this.to = to}Range.prototype.includes = function(x) {    return this,from <= x && x <= this.to}Range.prototype.toString = function() {    return this.from + ‘...‘ + this.to}var r1 = new Range(1, 3)console.log(r.includes(2))  // trueconsole.log(r.toString())  // 1...3

上述代碼聲明了一個Range建構函式,產生了類Range,使用new關鍵字能產生執行個體對象r1。

new Range(1, 3)這句運算式做了如下的事情:

var obj = {}obj.__proto__ = Range.prototypevar result = Range.call(obj, 1, 3)return typeof result === ‘obj‘ ? result : obj

第一步建立一個Null 物件。
第二步把新對象的__proto__設定為Range的prototype,即Range的原型。
第三步給Range建構函式傳參來給新對象的屬性賦值。
第四步檢測Range建構函式有沒有返回一個新對象,如果有就返回它,如果沒有就返回已經賦值好屬性的obj。
其實new關鍵字就是一個文法糖,類似於一個函數而已,我們使用了它,就相當於寫了上面這四句代碼。

new完後我們就可以說r1對象是Range類的執行個體,但建構函式只是類的公有標識,並不是唯一標識。也就是說兩個不同的建構函式的prototype屬性可以指向同一個原型對象,那麼它們建立的執行個體都是屬於同一個類的。所以原型對象才是類的唯一標識,下面介紹的檢測類型的方法也是依據執行個體對象是否繼承自某個原型對象,而不是檢測執行個體對象是由哪個建構函式初始化而來。
一共有三種檢測對象的類的技術:

  1. o instanceof Range
    如果o繼承自或者間接繼承自Range.prototype,那麼上面的運算式的結果為true
  2. Range.prototype.isPrototypeOf(o)
    如果o繼承自或者間接繼承自Range.prototype,那麼上面的運算式的結果為true
    *****
原型鏈和繼承

JS中實現繼承主要是依靠原型鏈來實現的。其基本思想是讓子類型的原型對象等於父類型的執行個體。而一般的繼承實現有三種方式:原型鏈繼承、借用建構函式繼承、組合繼承。

原型鏈繼承

function super() {    this.colors = ["red", "blue", "green"];}function sub() {}sub.prototype = new super();var instance1 = new sub();instance1.colors.push("black");alert(instance1.colors); //"red,blue,green,black"var instance2 = new sub();alert(instance2.colors); //"red,blue,green,black"

上述代碼把sub的原型對象賦值為super父類的執行個體對象,於是super執行個體的屬性就變為sub的原型屬性了。但這樣存在兩個問題:

  1. super執行個體屬性上如果存在參考型別值,那麼sub的原型對象屬性上也會有擁有相同的參考型別值。於是sub的所有執行個體對象都會共用其原型對象上的參考型別屬性。上述代碼中對instance1的改動就影響到了instance2。
  2. 在建立sub類型的執行個體時,不能向super類型的建構函式傳遞參數。也就是說super類型的建構函式如果需要用到參數,只能使用預設值。

借用建構函式
為瞭解決原型鏈繼承的參考型別值所帶來的問題,出現了一種稱為借用建構函式的技術(經典繼承)。其基本思想是在子類型建構函式的內部調用父類型的建構函式。

function super(name) {    this.colors = ["red", "blue", "green"];    this.name = name;}function sub() {    super.call(this, "jaja");}var instance1 = new sub();instance1.colors.push("black");alert(instance1.colors); //"red,blue,green,black"alert(instance1.name); // "jaja"var instance2 = new sub();alert(instance2.colors); //"red,blue,green"

雖然這種繼承方法“繼承”了父類的建構函式裡的執行個體屬性,但無法繼承父類的原型中定義的方法。單單繼承了建構函式中的屬性而無法複用父類的原型方法,那麼就不能稱得上是真正的繼承。

組合繼承
為了發揮上面兩張繼承方式的優點,技術人員又發明了組合繼承。其主要思想是使用原型鏈來實現對原型屬性和方法的繼承,而通過借用建構函式來實現對執行個體屬性的繼承。

function super() {    this.colors = ["red", "blue", "green"];}function sub() {    super.call(this);}sub.prototype = new super();sub.prototype.constructor = sub;var instance1 = new sub();instance1.colors.push("black");alert(instance1.colors); //"red,blue,green,black"var instance2 = new sub();alert(instance2.colors); //"red,blue,green"

組合繼承可以看作是對原型鏈繼承的增強,它與原型鏈繼承的主要不同是在子類的建構函式中引用了父類的建構函式。這樣即使使用sub.prototype = new super()繼承了父類的參考型別值,但後面在new sub()時通過sub的建構函式用執行個體屬性覆蓋了super的參考型別值,而super的原型對象的方法仍舊保留著沒被覆蓋。

JavaScript痛點系列(六):原型鏈與繼承

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.