摘要:繼承方式父類構(gòu)造函數(shù)實例方法子類調(diào)用實現(xiàn)父類的構(gòu)造函數(shù)吹呀吹呀,我的驕傲放縱
js構(gòu)造函數(shù)
*前言:上篇文章介紹了js中通過構(gòu)造函數(shù)來實例化對象的各種方法js構(gòu)造函數(shù),這篇文章主要介紹構(gòu)造函數(shù)的繼承(類的繼承),同樣包括 ES5 和 ES6 兩部分的介紹,能力所限,文中難免有不合理或錯誤的地方,還望各位大神批評指正~
原型首先簡單介紹一下實例屬性/方法 和 原型屬性/方法,以便更好理解下文
function Persion(name){ this.name = name; // 屬性 this.setName = function(nameName){ // 實例方法 this.name = newName; } } Persion.prototype.sex = "man"; // 向 Persion 原型中追加屬性(原型方法) var persion = new Persion("張三"); // 此時我們實例化一個persion對象,看一下name和sex有什么區(qū)別
在控制臺查看 persion 打印如下:
原來通過 prototype 添加的屬性將出現(xiàn)在實例對象的原型鏈中,
每個對象都會有一個內(nèi)置 proto 對象,當(dāng)在當(dāng)前對象中找不到屬性的時候就會在其原型鏈中查找(即原型鏈)
我們再來看下面的例子
注意:在構(gòu)造函數(shù)中,一般很少有數(shù)組形式的引用屬性,大部分情況都是:基本屬性 + 方法。
function Animal(n) { // 聲明一個構(gòu)造函數(shù) this.name = n; // 實例屬性 this.arr = []; // 實例屬性(引用類型) this.say = function(){ // 實例方法 return "hello world"; } } Animal.prototype.sing = function() { // 追加原型方法 return "吹呀吹呀,我的驕傲放縱~~"; } Animal.prototype.pArr = []; // 追加原型屬性(引用類型)
接下來我們看一下實例屬性/方法 和 原型屬性/方法的區(qū)別
原型對象的用途是為每個實例對象存儲共享的方法和屬性,它僅僅是一個普通對象而已。并且所有的實例是共享同一個原型對象,因此有別于實例方法或?qū)傩裕蛯ο髢H有一份。而實例有很多份,且實例屬性和方法是獨立的。
var cat = new Animal("cat"); // 實例化cat對象 var dog = new Animal("dog"); // 實例化狗子對象 cat.say === dog.say // false 不同的實例擁有不同的實例屬性/方法 cat.sing === dog.sing // true 不同的實例共享相同的原型屬性/方法 cat.arr.push("zz"); // 向cat實例對象的arr中追加元素;(私有) cat.pArr.push("xx"); // 向cat原型對象的pArr中追加元素;(共享) console.log(dog.arr); // 打印出 [],因為cat只改變了其私有的arr console.log(dog.pArr); // 打印出 ["xx"], 因為cat改變了與狗子(dog)共享的pArr
當(dāng)然,原型屬性為基本數(shù)據(jù)類型,則不會被共享
在構(gòu)造函數(shù)中:為了屬性(實例基本屬性)的私有性、以及方法(實例引用屬性)的復(fù)用、共享。我們提倡:
1、將屬性封裝在構(gòu)造函數(shù)中
2、將方法定義在原型對象上
首先,我們定義一個Animal父類
function Animal(n) { this.name = n; // 實例屬性 this.arr = []; // 實例屬性(引用類型) this.say = function(){ // 實例方法 return "hello world"; } } Animal.prototype.sing = function() { // 追加原型方法 return "吹呀吹呀,我的驕傲放縱~~"; } Animal.prototype.pArr = []; // 追加原型屬性(引用類型)1、原型鏈繼承
function Cat(n) { this.cName = n; } Cat.prototype = new Animal(); // 父類的實例作為子類的原型對象 var tom = new Cat("tom"); // 此時Tom擁有Cat和Animal的所有實例和原型方法/屬性,實現(xiàn)了繼承 var black = new Cat("black"); tom.arr.push("Im tom"); console.log(black.arr); // 打印出 ["Im tom"], 結(jié)果其方法變成了共享的,而不是每個實例所私有的,這是因為父類的實例方法/屬性變成了子類的原型方法/屬性了;
優(yōu)點: 實現(xiàn)了子對象對父對象的實例 方法/屬性 和 原型方法/屬性 的繼承;
缺點: 子類實例共享了父類構(gòu)造函數(shù)的引用數(shù)據(jù)類型屬性。
function Cat(n) { this.cName = n; Animal.call(this, this.cName); // 核心,把父類的實例方法屬性指向子類 } var tom = new Cat("tom"); // 此時Tom擁有Cat和Animal的所有實例和原型方法/屬性,實現(xiàn)了繼承 var black = new Cat("black"); tom.arr.push("Im tom"); console.log(black.arr); // 打印出 [], 其方法和屬性是每個子類實例所私有的; tom.sing(); // undefind 無法繼承父類的原型屬性及方法;
優(yōu)點:
1、實現(xiàn)了子對象對父對象的實例 方法/屬性 的繼承,每個子類實例所繼承的父類實例方法和屬性都是其私有的;
2、 創(chuàng)建子類實例,可以向父類構(gòu)造函數(shù)傳參數(shù);
缺點: 子類實例不能繼承父類的構(gòu)造屬性和方法;
function Cat(n) { this.cName = n; Animal.call(this, this.cName); // 核心,把父類的實例方法屬性指向子類 } Cat.prototype = new Parent() // 核心, 父類的實例作為子類的原型對象 Cat.prototype.constructor = Cat; // 修復(fù)子類Cat的構(gòu)造器指向,防止原型鏈的混亂 tom.arr.push("Im tom"); console.log(black.arr); // 打印出 [], 其方法和屬性是每個子類實例所私有的; tom.sing(); // 打印出 "吹呀吹呀,我的驕傲放縱~~"; 子類繼承了父類的原型方法及屬性
優(yōu)點:
1、創(chuàng)建子類實例,可以向父類構(gòu)造函數(shù)傳參數(shù);
2、父類的實例方法定義在父類的原型對象上,可以實現(xiàn)方法復(fù)用;
3、不共享父類的構(gòu)造方法及屬性;
缺點: 調(diào)用了2次父類的構(gòu)造方法
function Cat(n) { this.cName = n; Animal.call(this, this.cName); // 核心,把父類的實例方法屬性指向子類 } Cat.prototype = Parent.prototype; // 核心, 將父類原型賦值給子類原型(子類原型和父類原型,實質(zhì)上是同一個) Cat.prototype.constructor = Cat; // 修復(fù)子類Cat的構(gòu)造器指向,防止原型鏈的混亂 tom.arr.push("Im tom"); console.log(black.arr); // 打印出 [], 其方法和屬性是每個子類實例所私有的; tom.sing(); // 打印出 "吹呀吹呀,我的驕傲放縱~~"; 子類繼承了父類的原型方法及屬性 tom.pArr.push("publish"); // 修改繼承于父類原型屬性值 pArr; console.log(black.pArr); // 打印出 ["publish"], 父類的原型屬性/方法 依舊是共享的, // 至此簡直是完美呀~~~ 然鵝! Cat.prototype.childrenProp = "我是子類的原型屬性!"; var parent = new Animal("父類"); console.log(parent.childrenProp); // 打印出"我是子類的原型屬性!" what? 父類實例化的對象擁有子類的原型屬性/方法,這是因為父類和子類使用了同一個原型
優(yōu)點:
1、創(chuàng)建子類實例,可以向父類構(gòu)造函數(shù)傳參數(shù);
2、子類的實例不共享父類的構(gòu)造方法及屬性;
3、只調(diào)用了1次父類的構(gòu)造方法;
缺點: 父類和子類使用了同一個原型,導(dǎo)致子類的原型修改會影響父類;
function Cat(n) { this.cName = n; Animal.call(this, this.cName); // 核心,把父類的實例方法屬性指向子類; } var F = function(){}; // 核心,利用空對象作為中介; F.prototype = Parent.prototype; // 核心,將父類的原型賦值給空對象F; Cat.prototype = new F(); // 核心,將F的實例賦值給子類; Cat.prototype.constructor = Cat; // 修復(fù)子類Cat的構(gòu)造器指向,防止原型鏈的混亂; tom.arr.push("Im tom"); console.log(black.arr); // 打印出 [], 其方法和屬性是每個子類實例所私有的; tom.sing(); // 打印出 "吹呀吹呀,我的驕傲放縱~~"; 子類繼承了父類的原型方法及屬性; tom.pArr.push("publish"); // 修改繼承于父類原型屬性值 pArr; console.log(black.pArr); // 打印出 ["publish"], 父類的原型屬性/方法 依舊是共享的; Cat.prototype.childrenProp = "我是子類的原型屬性!"; var parent = new Animal("父類"); console.log(parent.childrenProp); // undefind 父類實例化的對象不擁有子類的原型屬性/方法;
優(yōu)點: 完美實現(xiàn)繼承;
缺點:實現(xiàn)相對復(fù)雜
function extend(Child, Parent) { var F = function(){}; F.prototype = Parent.prototype; hild.prototype = new F(); Child.prototype.constructor = Child; Child.uber = Parent.prototype; } // 使用 extend(Cat,Animal);
Child.uber = Parent.prototype; 的意思是為子對象設(shè)一個uber屬性,這個屬性直接指向父對象的prototype屬性。(uber是一個德語詞,意思是"向上"、"上一層"。)這等于在子對象上打開一條通道,可以直接調(diào)用父對象的方法。這一行放在這里,只是為了實現(xiàn)繼承的完備性,純屬備用性質(zhì)。
ES6繼承方式class Animal{ // 父類 constructor(name){ // 構(gòu)造函數(shù) this.name=name; } eat(){ // 實例方法 return "hello world"; } } class Cat extends Animal{ // 子類 constructor(name){ super(name); // 調(diào)用實現(xiàn)父類的構(gòu)造函數(shù) this.pName = name; } sing(){ return "吹呀吹呀,我的驕傲放縱~~"; } }
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/96288.html
摘要:中創(chuàng)建對象的方式有很多,尤其是基于原型的方式創(chuàng)建對象,是理解基于原型繼承的基礎(chǔ)。該函數(shù)中的屬性指向該源性對象當(dāng)通過該函數(shù)的構(gòu)造函數(shù)創(chuàng)建一個具體對象時,在這個對象中,就會有一個屬性指向原型。 js中創(chuàng)建對象的方式有很多,尤其是基于原型的方式創(chuàng)建對象,是理解基于原型繼承的基礎(chǔ)。因此在這里匯總一下,并對各種方法的利弊進行總結(jié)和對比,不至于以后對這些概念有模糊。 簡單方式創(chuàng)建 var o = ...
摘要:今年歲了調(diào)用私有方法實例化張三此時實例獲取不到方法追更感謝之染的評論,構(gòu)造函數(shù)還可以通過來添加對象栗子實例化張三此時實例化后的對象擁有方法輸出張三關(guān)于構(gòu)造函數(shù)的繼承可以看一下我的下一篇文章構(gòu)造函數(shù)繼承方法及利弊 js構(gòu)造函數(shù) 前言:之前看過公司大神的代碼,發(fā)現(xiàn)有很多構(gòu)造函數(shù),類似Java和C#的 new 方法來實例化一個對象,感覺很是受教,剛好最近在用es6,發(fā)現(xiàn)了用class來實現(xiàn)構(gòu)...
摘要:通過這種操作,就有了構(gòu)造函數(shù)的原型對象里的方法。你也看到了,就是一個普通對象,所以這種寄生式繼承適合于根據(jù)已有對象創(chuàng)建一個加強版的對象,在主要考慮通過已有對象來繼承而不是構(gòu)造函數(shù)的情況下,這種方式的確很方便。 原文地址在我的博客, 轉(zhuǎn)載請注明出處,謝謝! 標簽: [es5對象、原型, 原型鏈, 繼承] 注意(這篇文章特別長)這篇文章僅僅是我個人對于JavaScript對象的理解,并不是...
摘要:使用抽象基類顯示表示接口如果類的作用是定義接口,應(yīng)該將其明確定義為抽象基類。此外,抽象基類可以作為其他類的唯一基類,混入類則決不能作為唯一的基類,除非這個混入類繼承了另一個更具體的混入這種做法非常少見。 《流暢的Python》筆記本篇是面向?qū)ο髴T用方法的第五篇,我們將繼續(xù)討論繼承,重點說明兩個方面:繼承內(nèi)置類型時的問題以及多重繼承。概念比較多,較為枯燥。 1. 繼承內(nèi)置類型 內(nèi)置類型...
摘要:對象字面量創(chuàng)建對象張三學(xué)生這種方式的好處顯而易見,就是解決了之前的缺點。構(gòu)造函數(shù)模式張三學(xué)生李四學(xué)生與之前工廠模式的方法對比變量名首字母大寫了在函數(shù)內(nèi)沒有顯式的創(chuàng)建及返回對象而使用了創(chuàng)建時使用了關(guān)鍵字。 面向?qū)ο笫荍S的重點與難點,但也是走向掌握JS的必經(jīng)之路,有很多的文章或書籍中都對其進行了詳細的描述,本沒有必要再寫這些,但是對于學(xué)習(xí)來說,講給別人聽對自己來說是一種更好的受益方式。我...
閱讀 1701·2021-09-26 09:55
閱讀 3734·2021-09-22 15:31
閱讀 7427·2021-09-22 15:12
閱讀 2219·2021-09-22 10:02
閱讀 4692·2021-09-04 16:40
閱讀 1074·2019-08-30 15:55
閱讀 3031·2019-08-30 12:56
閱讀 1821·2019-08-30 12:44