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