首頁 >web前端 >js教程 >精講js建構函數的繼承方法及利弊(程式碼全)

精講js建構函數的繼承方法及利弊(程式碼全)

php是最好的语言
php是最好的语言原創
2018-07-26 18:19:511558瀏覽

這篇文章主要介紹構造函數的繼承(類別的繼承),同樣包括ES5 和ES6 兩部分的介紹,能力所限,文中難免有不合理或錯誤的地方,還望各位大神批評指正~

js建構子

原型

首先簡單介紹實例屬性/方法和原型屬性/方法,以便更好地理解下文

#
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 列印如下:
精講js建構函數的繼承方法及利弊(程式碼全)原來透過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、將方法定義在原型物件上

ES5繼承方式

##首先,我們定義一個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'], 结果其方法变成了共享的,而不是每个实例所私有的,这是因为父类的实例方法/属性变成了子类的原型方法/属性了;

優點: 實作了子物件對父物件的實例方法/屬性和原型方法/屬性的繼承;
缺點: 子類別實例共用了父類別建構子的參考資料型別屬性。

2、借用建構子

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、建立子類別實例,可以傳送參數給父類別向父類別;
#缺點: 子類別實例不能繼承父類別的建構屬性和方法;

3、組合繼承

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次父類別的建構方法;
缺點: 父類別和子類別使用了同一個原型,導致子類別的原型修改會影響父類別;

5、寄生組合繼承(簡直完美)

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  父类实例化的对象不拥有子类的原型属性/方法;

優點: 完美實現繼承;
缺點:實作相對複雜

附YUI函式庫實作繼承

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 '吹呀吹呀,我的骄傲放纵~~';
  }
}

相關文章:

php建構子的繼承方法,php建構子繼承

相關視頻:

極客學院PHP物件導向影片教學

以上是精講js建構函數的繼承方法及利弊(程式碼全)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn