這篇文章主要介紹構造函數的繼承(類別的繼承),同樣包括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次父類別的建構方法
4、寄生組合繼承
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是個德語詞,意思是"向上"、"上一層"。)這等於在子物件上開啟一條通道,可以直接呼叫父物件的方法。這一行放在這裡,只是為了實現繼承的完備性,純屬備用性質。
class Animal{ // 父类 constructor(name){ // 构造函数 this.name=name; } eat(){ // 实例方法 return 'hello world'; } } class Cat extends Animal{ // 子类 constructor(name){ super(name); // 调用实现父类的构造函数 this.pName = name; } sing(){ return '吹呀吹呀,我的骄傲放纵~~'; } }
相關文章:
相關視頻:
以上是精講js建構函數的繼承方法及利弊(程式碼全)的詳細內容。更多資訊請關注PHP中文網其他相關文章!