摘要:如何確定原型和實例的關(guān)系第一個方法是,,用于檢測實例與原型鏈中出現(xiàn)過的構(gòu)造函數(shù)。所謂寄生組合繼承,即通過借用構(gòu)造函數(shù)方式,繼承屬性,通過原型鏈形式繼承方法。
概述
原型和閉包是JS的兩個難點,最近碰到了原型繼承的概念,正好在這里總結(jié)一下。
既然要實現(xiàn)繼承,就一定要有一個父類。
// 定義一個父類 function father(name) { //屬性 this.name = name; } // 原型方法 father.prototype.getName = function () { return this.name; }原型鏈繼承
基本思想就是利用原型讓一個引用類型繼承另一個引用類型的屬性和方法。
回顧一下原型、實例和構(gòu)造函數(shù)的關(guān)系。
每個構(gòu)造函數(shù)都有一個原型對象,原型對象都包含一個指向構(gòu)造函數(shù)的指針,而實例都包含一個指向原型對象內(nèi)部的指針。
// 子類 function son(age) { // 屬性 this.age = age; }; son.prototype = new father("jason"); son.prototype.getAge = function () { return this.age; } let firstchild = new son("19"); console.log(firstchild.getAge()) // 19
這里需要注意幾點的是:
默認(rèn)原型
原型鏈的最頂端是Object,所有引用類型默認(rèn)都是繼承于Object的,所以默認(rèn)也是有toString等方法的。
如何確定原型和實例的關(guān)系
第一個方法是,instanceof,用于檢測實例與原型鏈中出現(xiàn)過的構(gòu)造函數(shù)。
console.log(firstchild instanceof Object) //true console.log(firstchild instanceof son) //true console.log(firstchild instanceof father) //true
第二個方法是,isPrototypeOf方法。
console.log(Object.prototype.isPrototypeOf(firstchild)) //true console.log(son.prototype.isPrototypeOf(firstchild)) //true console.log(father.prototype.isPrototypeOf(firstchild)) //true
謹(jǐn)慎定義方法
子類型可能要重寫父類型方法,或定義父類沒有的方法。不管是啥,這個方法一定要寫在替換原型語句的后面。
還有原型鏈繼承的時候,不能使用對象字面量創(chuàng)建原型方法。
例如:
son.prototype = new father("jason"); son.prototype = { getAge: function() { return this.age } }
這樣會導(dǎo)致創(chuàng)建一個新的Object實例,而非原來的father。
共享性和傳參問題
第一,引用類型的原型屬性會被所有實例共享。
function father(name) { this.name = name; this.colors = ["blue", "red", "white"]; } let firstchild = new son("19"); let secondchild = new son("20"); firstchild.colors.push("black"); console.log(firstchild.colors) // ["blue", "red", "white", "black"] console.log(secondchild.colors) // ["blue", "red", "white", "black"]
第二,不能像父類型構(gòu)造函數(shù)傳參數(shù),書里準(zhǔn)確說法是,沒有辦法在不影響所有實例的情況下,給父類構(gòu)造函數(shù)傳遞參數(shù)。
小結(jié)優(yōu)點:
非常純粹的繼承關(guān)系,實例是子類的實例,也是父類的實例
父類新增原型方法/原型屬性,子類都能訪問到
簡單,易于實現(xiàn)
缺點:
要想為子類新增屬性和方法,必須要在new father()這樣的語句之后執(zhí)行,不能放到構(gòu)造器中
無法實現(xiàn)多繼承
來自原型對象的引用屬性是所有實例共享的
創(chuàng)建子類實例時,無法向父類構(gòu)造函數(shù)傳參
借用構(gòu)造繼承在子類型的構(gòu)造函數(shù)中調(diào)用父類的構(gòu)造函數(shù),使用父類的構(gòu)造函數(shù)來增強子類實例,等于是復(fù)制父類的實例屬性給子類(不用原型)
function son(age) { father.call(this); this.age = age; }; son.prototype = new father("jason"); son.prototype.getAge = function () { return this.age; } let firstchild = new son("19"); let secondchild = new son("20"); firstchild.colors.push("black"); console.log(firstchild.colors); // ["blue", "red", "white", "black"] console.log(secondchild.colors); // ["blue", "red", "white"]
可以傳遞參數(shù)
方法都在構(gòu)造函數(shù)中定義,函數(shù)復(fù)用性丟失
總結(jié)優(yōu)點:
由例子可見,解決了1中子類實例共享父類引用屬性的問題
創(chuàng)建子類實例時,可以向父類傳遞參數(shù)
可以實現(xiàn)多繼承(call多個父類對象)
缺點:
實例并不是父類的實例,只是子類的實例
只能繼承父類的實例屬性和方法,不能繼承原型屬性/方法
無法實現(xiàn)函數(shù)復(fù)用,每個子類都有父類實例函數(shù)的副本,影響性能
組合繼承也就是將原型鏈繼承和構(gòu)造函數(shù)繼承融合,原型鏈實現(xiàn)對原型屬性和方法的繼承,構(gòu)造函數(shù)實現(xiàn)對實例屬性的繼承。
這樣既保證了原型上函數(shù)的復(fù)用,也保證了每個實例有自己的屬性。
function son(name, age) { father.call(this, name); this.age = age; }; son.prototype = new father(); son.prototype.getAge = function () { return this.age; } let firstchild = new son("jason", "19"); let secondchild = new son("jason junior", "18"); firstchild.colors.push("black"); console.log(firstchild.colors); // ["blue", "red", "white", "black"] console.log(secondchild.colors); //["blue", "red", "white"] console.log(firstchild.getName()); // jason console.log(secondchild.getName()); // jason junior console.log(firstchild.getAge()); //19 console.log(secondchild.getAge()); //18
特點:
可以繼承實例屬性/方法,也可以繼承原型屬性/方法
既是子類的實例,也是父類的實例
不存在引用屬性共享問題
可傳參
函數(shù)可復(fù)用
缺點:
調(diào)用了兩次父類構(gòu)造函數(shù),生成了兩份實例(子類實例將子類原型上的那份屏蔽了)
原型式繼承為父類實例添加新特性,作為子類實例返回
let p = { name: "jason", colors: ["white", "black", "red"] } function object (o) { function F() {}; F.prototype = o; return new F(); } let firstchild = object(p) let secondchild = object(p) firstchild.name = "jason1" firstchild.colors.push("blue") secondchild.name = "jason2" secondchild.colors.push("green") console.log(p.colors) //?["white", "black", "red", "blue", "green"]
ECMAScript 5新增Object.create()方法規(guī)范原型式繼承。兩個參數(shù),一個參數(shù)是新對象原型的對象,一個參數(shù)是對象定義額外屬性的對象,第二個可忽略,就等于上述object函數(shù)了
寄生式繼承創(chuàng)造一個用于封裝繼承過程的函數(shù),該函數(shù)內(nèi)部以某種方式增強對象。
function create(o) { let clone = object(o); o.sayHi = function () { console.log("Hi") } return o; }寄生組合繼承
組合繼承雖然好用,但是也有缺陷,就是會調(diào)用兩次構(gòu)造函數(shù),一次在創(chuàng)建時候,一次在內(nèi)部,那個call方法。
所謂寄生組合繼承,即通過借用構(gòu)造函數(shù)方式,繼承屬性,通過原型鏈形式繼承方法。
沿用寄生方式:
function inheritPrototype (sub, sup) { let prototype = object(sup.prototype); prototype.constructor = sub; sub.prototype = prototype; }
function father(name) { this.name = name; this.colors = ["blue", "red", "white"]; } father.prototype.getName = function () { return this.name; } function son(name, age) { father.call(this, name); this.age = age; }; function object (o) { function F() {}; F.prototype = o; return new F(); } function inheritPrototype (sub, super) { let prototype = object(super.prototype); prototype.constructor = sub; sub.prototype = prototype; } inheritPrototype(son, father); son.prototype.getAge = function () { return this.age; }總結(jié)
優(yōu)點:
堪稱完美
缺點:
實現(xiàn)較為復(fù)雜
參考 <>總結(jié)
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/95002.html
摘要:對象創(chuàng)建的三種方式字面量創(chuàng)建方式系統(tǒng)內(nèi)置構(gòu)造函數(shù)方式自定義構(gòu)造函數(shù)構(gòu)造函數(shù)原型實例之間的關(guān)系實例是由構(gòu)造函數(shù)實例化創(chuàng)建的,每個函數(shù)在被創(chuàng)建的時候,都會默認(rèn)有一個對象。 JS 對象創(chuàng)建的三種方式 //字面量創(chuàng)建方式 var person= { name:jack } //系統(tǒng)內(nèi)置構(gòu)造函數(shù)方式 var person= new Object(); person.name = jack; ...
摘要:繼承了如上,我們通過方法借調(diào)了超類的構(gòu)造函數(shù),實際上是在新創(chuàng)建的實力環(huán)境下調(diào)用了構(gòu)造函數(shù)。組合繼承組合繼承的基本思想將原型鏈和借用構(gòu)造函數(shù)的技術(shù)組合到一塊,從而發(fā)揮二者之長的一種繼承模式。繼承方法在上面這個例子中,構(gòu)造函數(shù)定義了兩個屬性和。 在ECMAScript中只支持實現(xiàn)繼承,而且實現(xiàn)繼承主要是依靠原型鏈來實現(xiàn)的。 1. 什么是原型鏈 繼承基本思想:利用原型讓一個引用類型繼承另一個...
摘要:組合模式繼承結(jié)合了構(gòu)造函數(shù)繼承時可以為每個屬性重新初始化,構(gòu)造一個副本的優(yōu)點,以及原型鏈繼承時一次定義處處共享的優(yōu)點。但令我百思不得其解的是,從上面給出的例子來看,組合繼承并沒有調(diào)用兩次超類型構(gòu)造函數(shù)。 最近在閱讀《js權(quán)威指南》的繼承這一章,對于組合模式和寄生組合模式的區(qū)別有點混淆,在多次重讀以及嘗試之后,得到一些心得。 組合模式繼承 結(jié)合了構(gòu)造函數(shù)繼承時可以為每個屬性重新初始化,構(gòu)...
摘要:關(guān)于中面向?qū)ο蟮睦斫饷嫦驅(qū)ο缶幊趟且环N編程思想我們的編程或者學(xué)習(xí)其實是按照類實例來完成的學(xué)習(xí)類的繼承封裝多態(tài)封裝把實現(xiàn)一個功能的代碼封裝到一個函數(shù)中一個類中以后再想實現(xiàn)這個功能,只需要執(zhí)行這個函數(shù)方法即可,不需要再重復(fù)的編寫代碼。 關(guān)于js中面向?qū)ο蟮睦斫?面向?qū)ο缶幊?oop) 它是一種編程思想 (object-oriented programming ), 我們的編程或者學(xué)習(xí)其...
摘要:關(guān)于中面向?qū)ο蟮睦斫饷嫦驅(qū)ο缶幊趟且环N編程思想我們的編程或者學(xué)習(xí)其實是按照類實例來完成的學(xué)習(xí)類的繼承封裝多態(tài)封裝把實現(xiàn)一個功能的代碼封裝到一個函數(shù)中一個類中以后再想實現(xiàn)這個功能,只需要執(zhí)行這個函數(shù)方法即可,不需要再重復(fù)的編寫代碼。 關(guān)于js中面向?qū)ο蟮睦斫?面向?qū)ο缶幊?oop) 它是一種編程思想 (object-oriented programming ), 我們的編程或者學(xué)習(xí)其...
閱讀 2317·2021-11-15 11:38
閱讀 2447·2021-11-15 11:37
閱讀 2552·2021-08-24 10:00
閱讀 2912·2019-08-30 15:56
閱讀 1267·2019-08-30 15:53
閱讀 3707·2019-08-29 18:43
閱讀 2935·2019-08-29 17:01
閱讀 3259·2019-08-29 16:25