摘要:寄生式繼承的思路與寄生構造函數和工廠模式類似,即創建一個僅用于封裝繼承過程的函數,該函數在內部已某種方式來增強對象,最后再像真的是它做了所有工作一樣返回對象。
這篇本來應該是作為寫JS 面向對象的前奏,只是作為《javascript高級程序設計》繼承一章的筆記
原型鏈
code 實現
function SuperType() { this.colors = ["red","blue", "green"]; } function SubType() { } SubType.prototype = new SuperType(); var instance1 = new SubType(); instance1.colors.push("black"); console.log(instance1.colors); // ["red","blue", "green","black"] var instance2 = new SubType(); console.log(instance2.colors); // ["red","blue", "green","black"] var instance = new SuperType(); console.log(instance.colors); // ["red","blue", "green"]
使用原型鏈來實現繼承,原型實際上會變成另一個類型的實例,于是,原先的實例屬性,會變成現在的原型屬性了
在創建子類的實例時,不能向父類的構造函數中傳遞參數
借用構造函數
code 實現繼承
function SuperType() { this.colors = ["red","blue","green"]; } function SubType() { SuperType.call(this); } var instance1 = new SubType(); instance1.colors.push("black"); console.log(instance1.colors); // ["red", "blue", "green", "black"] var instance2 = new SubType(); console.log(instance2.colors); // ["red", "blue", "green"] var instance = new SuperType(); console.log(instance.colors); // ["red","blue", "green"]
同樣也可以實現參數的傳遞
function SuperType(name) { this.name = name; } function SubType(){ SuperType.call(this, "jack"); this.age = 29; } var instance = new SubType(); console.log(instance.name); // jack console.log(instance.age); // 29
如果僅僅是借用構造函數,那么將無法避免構造函數模式存在的問題--方法都在構造函數中定義,因此,函數復用也就無從談起了。而且,在超類型的原型中定義的方法,對子類而言也是不可見的,結果所有類型都只能使用構造函數模式。
組合繼承
將原型鏈和借用構造函數的技術組合到一塊,從而發回二者之長的一種繼承模式。其背后的思路是使用原型鏈實現對原型屬性和方法的繼承,而通過借用構造函數來實現對實例屬性的繼承。這樣,即通過在原型上定義方法實現了函數復用,又能夠保證每個實例都有它自己的屬性。
code 實現
function SuperType(name) { this.name = name; this.colors = ["red","blue","green"]; } SuperType.prototype.sayName = function() { console.log(this.name); }; function SubType(name, age) { SuperType.call(this, name); this.age = age; }; SubType.prototype = new SuperType(); SubType.prototype.sayAge = function(){ console.log(this.age); }; var instance1 = new SubType("jack", 29); instance1.colors.push("black"); console.log(instance1.colors); //["red", "blue", "green", "black"] instance1.sayName(); // jack instance1.sayAge(); // 29 var instance2 = new SubType("allen", 23); console.log(instance2.colors); // ["red", "blue", "green"] instance2.sayName(); //allen instance2.sayAge(); // 23
instanceOf和isPrototypeOf 也能夠用于識別基于組合繼承創建的對象
原型式繼承
沒有嚴格意義上的構造函數,通過借助原型,可以基于已有的對象創建新對象,同時還不必因此創建自定義類型
function object(o){ function F(){}; F.prototype = o; return new F(); }
在object() 函數內部,先創建了一個臨時性的構造函數,然后將傳入的對象作為這個構造函數的原型,最后返回這個臨時類型的一個新實例。從本質上將,object() 對傳入其中的對象執行了一次淺復制
function object(o) { function F() {}; F.prototype = o; return new F(); } var person = { name:"jack", friends:["allen","lucy","van"] } var anotherPerson = object(person); anotherPerson.name = "bob"; anotherPerson.friends.push("steve"); var yetAnotherPerson = object(person); yetAnotherPerson.name = "linda"; yetAnotherPerson.friends.push("shelly") console.log(person.friends); //["allen", "lucy", "van", "steve", "shelly"]
這種原型式繼承,要求你必須有一個對象可以作為另一個對象的基礎。如果有這么一個對象的話,可以把他傳遞給object() 函數,然后再根據具體需求對得到的對象加以修飾即可。
ECMAScript5 通過Object.create() 方法規范花了原型式繼承。這個方法接受兩個參數:一個用作新對象原型的對象和(可選的)一個為新對象定義額外屬性的對象。在傳入一個參數的情況下,Object,create() 與object() 函數方法的行為相同
在沒有必要創建構造函數,而只是想讓一個對象與另一個對象保持類似的情況下,原型式繼承是完全可以勝任的。不過,包含引用類型值的屬性始終都會共享響應的值,就像使用原型模式一樣
寄生式繼承
寄生式繼承是與原型式繼承緊密相關的一種思路。寄生式繼承的思路與寄生構造函數和工廠模式類似,即創建一個僅用于封裝繼承過程的函數,該函數在內部已某種方式來增強對象,最后再像真的是它做了所有工作一樣返回對象。
function createAnother(original){ var clone = object(original); // 通過調用 object() 函數創建一個新對象 clone.sayHi = function(){ // 以某種方式來增強對象 alert("hi"); }; return clone; // 返回這個對象 }
在主要考慮對象而不是自定義類型和構造函數的情況下,寄生式繼承也是一種有用的模式。前面示范繼承模式時使用的object() 函數不是必需的;任何能夠返回新對象的函數都使用與此模式。
寄生組合式繼承
組合繼承是JavaScript 最常用的繼承模式;不過也有自己的不足。組合繼承最大的問題就是無論什么情況下,都會調用兩次父類的構造函數:一次是在創建子類原型的時候,另一次是在子類構造函數內部。
子類最終會包含父類對象的全部實例屬性,但我們不得不在調用子類構造函數時重寫這些屬性。
function SuperType(name) { this.name = name; this.colors = ["red","blue","green"]; } SuperType.prototype.sayName = function(){ console.log(this.name); } function SubType(name, age) { SuperType.call(this, name); // 第二次調用 SuperType() this.age = age; } SubType.prototype = new SuperType(); // 第一次調用 SuperType() SubType.prototype.constructor = SubType; SubType.prototype.sayAge = function() { console.log(this.age); };
第一次調用SuperType 構造函數時,SubType.prototype會得到兩個屬性:name 和colors,他們都是SuperType 的實例屬性,只不過現在位于SubType 的原型中。當調用SubType 構造函數時,又會調用一次SuperType 構造函數,這一次又在新對象上創建了實例屬性name 和colors。 于是,這兩個屬性就屏蔽了原型中的兩個同名屬性。
?
所謂寄生組合式繼承,即通過借用構造函數來繼承屬性,通過原型鏈的混成形式來繼承方法。其背后的基本思路是:不必為了指定子類的原型而調用父類的構造函數,我們所需要的無非就是父類原型的一個副本而已。本質上,就是使用寄生式繼承來繼承父類型的原型,然后再將結果指定給子類的原型。寄生組合式繼承的基本模式如下
function inheritPrototype(subType, superType) { var prototype = object(superType.prototype); prototype.constructor = subType; subType.prototype = prototype; }
第一步是創建父類原型的一個副本,第二步是為創建的副本添加constructor 屬性,從而彌補因重寫原型而失去的默認的constructor 屬性。第三步是將新創建的對象(即副本)賦值給子類的原型。
function SuperType(name) { this.name = name; this.colors = ["red","blue","green"]; } SuperType.prototype.sayName = function(){ console.log(this.name); } function SubType(name, age) { SuperType.call(this, name); // 第二次調用 SuperType() this.age = age; } inheritPrototype(SubType, SuperType) SubType.prototype.sayAge = function() { console.log(this.age); };
這個例子的高效率體現在它只調用了一次SuperType 構造函數,并且因此避免了在SubType.prototype 上創建不必要的、多余的屬性。于此同時,原型鏈還能保持不變;因此,還能夠正常使用instanceof 和isPrototypeOf() 。開發人員普遍認為寄生組合繼承是引用類型最理想的繼承范式。
優缺點原型鏈會修改父類的屬性,在創建子類的實例時,不能向父類的構造函數中傳遞參數
借用構造函數,則沒有繼承
組合繼承(原型繼承+借用構造函數) 組合繼承最大的問題就是無論什么情況下,都會調用兩次父類的構造函數:一次是在創建子類原型的時候,另一次是在子類構造函數內部。
原型式繼承,要求你必須有一個對象可以作為另一個對象的基礎,
包含引用類型值的屬性始終都會共享響應的值,就像使用原型模式一樣
寄生式繼承
在主要考慮對象而不是自定義類型和構造函數的情況下,寄生式繼承也是一種有用的模式
寄生組合式繼承 (這是最成熟的方法,也是現在庫實現的方法)
第一步是創建父類原型的一個副本,第二步是為創建的副本添加constructor?屬性,從而彌補因重寫原型而失去的默認的constructor?屬性。第三步是將新創建的對象(即副本)賦值給子類的原型。
?
JS面向對象系列
prototype.js 是如何實現JS的類以及類的相關屬性和作用
Mootools.js 是如何實現類,以及類的相關屬性和作用
klass 是如何實現JS的類以及類的相關屬性和作用
總結:prototype.js,Mootools.js和klass.js 實現類的方法的異同與優劣
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/84547.html
摘要:繼承和前面兩篇文章中的知識非常相關,如果對函數創建原理和原型鏈不熟悉,請猛戳高級程序設計筆記創建對象高級程序設計筆記原型圖解繼承,通俗的說,就是將自身不存在的屬性或方法,通過某種方式為自己所用文章分別介紹原型鏈繼承繼承借用構造函數繼承組合繼 繼承和前面兩篇文章中的知識非常相關,如果對函數創建原理和原型鏈不熟悉,請猛戳:《javascript高級程序設計》筆記:創建對象《javascri...
摘要:上一篇你不知道的筆記寫在前面這是年第一篇博客,回顧去年年初列的學習清單,發現僅有部分完成了。當然,這并不影響年是向上的一年在新的城市穩定連續堅持健身三個月早睡早起游戲時間大大縮減,學會生活。 上一篇:《你不知道的javascript》筆記_this 寫在前面 這是2019年第一篇博客,回顧去年年初列的學習清單,發現僅有部分完成了。當然,這并不影響2018年是向上的一年:在新的城市穩定、...
摘要:面向對象高級繼承模式一原型鏈繼承方式原型鏈繼承流程定義父類型構造函數。缺點無法避免構造函數模式存在的問題方法都在構造函數中定義,無法函數復用。六寄生組合式繼承在這里重復一下組合繼承的代碼組合繼承最大的缺點是會調用兩次父構造函數。 JavaScript 面向對象高級——繼承模式 一、原型鏈繼承 方式1: 原型鏈繼承 (1)流程: ? 1、定義父類型構造函數。 ? ...
摘要:推薦高級程序設計,對類繼承有詳細介紹。書中涉及繼承方式多達數種,意味著繼承的靈活性。假設類和類不同公司有不同的公司信息,而同一公司內的員工則需要繼承相同的公司信息。組合繼承組合繼承可以認為是以上兩種組合實現。 前言 高級語言基本上都有類的概念,而javascript因為各種原因相對比較特別,并沒有明確的class類聲明方式(ES6暫不涉及),而是通過構造函數變相實現。推薦《javas...
摘要:繼承的是超類型中構造函數中的屬性,如上繼承了屬性,但沒有繼承原型中的方法。上述造成的結果是子類型實例中有兩組超類型的構造函數中定義的屬性,一組在子類型的實例中,一組在子類型實例的原型中。 ECMAScript只支持實現繼承,主要依靠原型鏈來實現。與實現繼承對應的是接口繼承,由于script中函數沒有簽名,所以無法實現接口繼承。 一、原型鏈 基本思想:利用原型讓一個引用類型繼承另一個引用...
閱讀 2543·2023-04-26 00:56
閱讀 2000·2021-10-25 09:46
閱讀 1236·2019-10-29 15:13
閱讀 811·2019-08-30 15:54
閱讀 2190·2019-08-29 17:10
閱讀 2611·2019-08-29 15:43
閱讀 497·2019-08-29 15:28
閱讀 3022·2019-08-29 13:24