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