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