摘要:繼承方式父類構造函數實例方法子類調用實現父類的構造函數吹呀吹呀,我的驕傲放縱
js構造函數
*前言:上篇文章介紹了js中通過構造函數來實例化對象的各種方法js構造函數,這篇文章主要介紹構造函數的繼承(類的繼承),同樣包括 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有什么區別
在控制臺查看 persion 打印如下:
原來通過 prototype 添加的屬性將出現在實例對象的原型鏈中,
每個對象都會有一個內置 proto 對象,當在當前對象中找不到屬性的時候就會在其原型鏈中查找(即原型鏈)
我們再來看下面的例子
注意:在構造函數中,一般很少有數組形式的引用屬性,大部分情況都是:基本屬性 + 方法。
function Animal(n) { // 聲明一個構造函數 this.name = n; // 實例屬性 this.arr = []; // 實例屬性(引用類型) this.say = function(){ // 實例方法 return "hello world"; } } Animal.prototype.sing = function() { // 追加原型方法 return "吹呀吹呀,我的驕傲放縱~~"; } Animal.prototype.pArr = []; // 追加原型屬性(引用類型)
接下來我們看一下實例屬性/方法 和 原型屬性/方法的區別
原型對象的用途是為每個實例對象存儲共享的方法和屬性,它僅僅是一個普通對象而已。并且所有的實例是共享同一個原型對象,因此有別于實例方法或屬性,原型對象僅有一份。而實例有很多份,且實例屬性和方法是獨立的。
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
當然,原型屬性為基本數據類型,則不會被共享
在構造函數中:為了屬性(實例基本屬性)的私有性、以及方法(實例引用屬性)的復用、共享。我們提倡:
1、將屬性封裝在構造函數中
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的所有實例和原型方法/屬性,實現了繼承 var black = new Cat("black"); tom.arr.push("Im tom"); console.log(black.arr); // 打印出 ["Im tom"], 結果其方法變成了共享的,而不是每個實例所私有的,這是因為父類的實例方法/屬性變成了子類的原型方法/屬性了;
優點: 實現了子對象對父對象的實例 方法/屬性 和 原型方法/屬性 的繼承;
缺點: 子類實例共享了父類構造函數的引用數據類型屬性。
function Cat(n) { this.cName = n; Animal.call(this, this.cName); // 核心,把父類的實例方法屬性指向子類 } var tom = new Cat("tom"); // 此時Tom擁有Cat和Animal的所有實例和原型方法/屬性,實現了繼承 var black = new Cat("black"); tom.arr.push("Im tom"); console.log(black.arr); // 打印出 [], 其方法和屬性是每個子類實例所私有的; tom.sing(); // undefind 無法繼承父類的原型屬性及方法;
優點:
1、實現了子對象對父對象的實例 方法/屬性 的繼承,每個子類實例所繼承的父類實例方法和屬性都是其私有的;
2、 創建子類實例,可以向父類構造函數傳參數;
缺點: 子類實例不能繼承父類的構造屬性和方法;
function Cat(n) { this.cName = n; Animal.call(this, this.cName); // 核心,把父類的實例方法屬性指向子類 } Cat.prototype = new Parent() // 核心, 父類的實例作為子類的原型對象 Cat.prototype.constructor = Cat; // 修復子類Cat的構造器指向,防止原型鏈的混亂 tom.arr.push("Im tom"); console.log(black.arr); // 打印出 [], 其方法和屬性是每個子類實例所私有的; tom.sing(); // 打印出 "吹呀吹呀,我的驕傲放縱~~"; 子類繼承了父類的原型方法及屬性
優點:
1、創建子類實例,可以向父類構造函數傳參數;
2、父類的實例方法定義在父類的原型對象上,可以實現方法復用;
3、不共享父類的構造方法及屬性;
缺點: 調用了2次父類的構造方法
function Cat(n) { this.cName = n; Animal.call(this, this.cName); // 核心,把父類的實例方法屬性指向子類 } Cat.prototype = Parent.prototype; // 核心, 將父類原型賦值給子類原型(子類原型和父類原型,實質上是同一個) Cat.prototype.constructor = Cat; // 修復子類Cat的構造器指向,防止原型鏈的混亂 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? 父類實例化的對象擁有子類的原型屬性/方法,這是因為父類和子類使用了同一個原型
優點:
1、創建子類實例,可以向父類構造函數傳參數;
2、子類的實例不共享父類的構造方法及屬性;
3、只調用了1次父類的構造方法;
缺點: 父類和子類使用了同一個原型,導致子類的原型修改會影響父類;
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; // 修復子類Cat的構造器指向,防止原型鏈的混亂; 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 父類實例化的對象不擁有子類的原型屬性/方法;
優點: 完美實現繼承;
缺點:實現相對復雜
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; 的意思是為子對象設一個uber屬性,這個屬性直接指向父對象的prototype屬性。(uber是一個德語詞,意思是"向上"、"上一層"。)這等于在子對象上打開一條通道,可以直接調用父對象的方法。這一行放在這里,只是為了實現繼承的完備性,純屬備用性質。
ES6繼承方式class Animal{ // 父類 constructor(name){ // 構造函數 this.name=name; } eat(){ // 實例方法 return "hello world"; } } class Cat extends Animal{ // 子類 constructor(name){ super(name); // 調用實現父類的構造函數 this.pName = name; } sing(){ return "吹呀吹呀,我的驕傲放縱~~"; } }
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/96288.html
摘要:中創建對象的方式有很多,尤其是基于原型的方式創建對象,是理解基于原型繼承的基礎。該函數中的屬性指向該源性對象當通過該函數的構造函數創建一個具體對象時,在這個對象中,就會有一個屬性指向原型。 js中創建對象的方式有很多,尤其是基于原型的方式創建對象,是理解基于原型繼承的基礎。因此在這里匯總一下,并對各種方法的利弊進行總結和對比,不至于以后對這些概念有模糊。 簡單方式創建 var o = ...
摘要:通過這種操作,就有了構造函數的原型對象里的方法。你也看到了,就是一個普通對象,所以這種寄生式繼承適合于根據已有對象創建一個加強版的對象,在主要考慮通過已有對象來繼承而不是構造函數的情況下,這種方式的確很方便。 原文地址在我的博客, 轉載請注明出處,謝謝! 標簽: [es5對象、原型, 原型鏈, 繼承] 注意(這篇文章特別長)這篇文章僅僅是我個人對于JavaScript對象的理解,并不是...
摘要:使用抽象基類顯示表示接口如果類的作用是定義接口,應該將其明確定義為抽象基類。此外,抽象基類可以作為其他類的唯一基類,混入類則決不能作為唯一的基類,除非這個混入類繼承了另一個更具體的混入這種做法非常少見。 《流暢的Python》筆記本篇是面向對象慣用方法的第五篇,我們將繼續討論繼承,重點說明兩個方面:繼承內置類型時的問題以及多重繼承。概念比較多,較為枯燥。 1. 繼承內置類型 內置類型...
摘要:對象字面量創建對象張三學生這種方式的好處顯而易見,就是解決了之前的缺點。構造函數模式張三學生李四學生與之前工廠模式的方法對比變量名首字母大寫了在函數內沒有顯式的創建及返回對象而使用了創建時使用了關鍵字。 面向對象是JS的重點與難點,但也是走向掌握JS的必經之路,有很多的文章或書籍中都對其進行了詳細的描述,本沒有必要再寫這些,但是對于學習來說,講給別人聽對自己來說是一種更好的受益方式。我...
閱讀 1684·2021-09-26 09:55
閱讀 3713·2021-09-22 15:31
閱讀 7328·2021-09-22 15:12
閱讀 2209·2021-09-22 10:02
閱讀 4625·2021-09-04 16:40
閱讀 1031·2019-08-30 15:55
閱讀 3018·2019-08-30 12:56
閱讀 1813·2019-08-30 12:44