實現繼承的幾種方式及工作原理

南果梨發表於2018-10-29
  • 原型鏈繼承

    核心:子建構函式的原型指向父建構函式的例項

    每個建構函式都有一個原型物件,原型物件中都包含一個指向建構函式的指標,而例項都包含一個指向原型物件的內部指標。當原型物件等於另外一個型別的例項即繼承。呼叫某個方法或者屬性的步驟

    a.搜尋例項

    b.搜尋原型

    c.搜尋父類原型


        function Animal() {
            this.name = "animal";
            this.arrt = [1, 2]
        }
        Animal.prototype = {
            sayName: function () {
                alert(this.name);
            }
        }

        function Dog() {
            this.color = "灰色"
        }

        Dog.prototype = new Animal();
        Dog.prototype.sayColor = function () {
            alert(this.color);
        }
        var dog = new Dog();
        var dog1 = new Dog();
        console.log(dog);
        dog.arrt.push(5);
        console.log(dog1);
        dog.sayColor();
        dog.sayName();
複製程式碼

可以自己列印一下,看一下結果

實現繼承的幾種方式及工作原理
優點

1.非常純粹的繼承關係,例項是子類的例項,也是父類的例項

2.父類新增原型方法/原型屬性,子類都能訪問到

3.簡單,易於實現

缺點

1.包含引用型別值的原型屬性會被所有例項共享,這會導致對一個例項的修改會影響另一個例項。

2.在建立子型別的例項時,不能向超型別的建構函式中傳遞引數。由於這兩個問題的存在,實踐中很少單獨使用原型鏈。

  • 借用建構函式

    也稱 "偽造物件" 或 "經典繼承",在子型別建構函式的內部呼叫超型別建構函式。函式不過是在特定環境中執行程式碼的物件,因此通過apply(),call()方法可以在(將來)新建物件上執行建構函式,即 在子型別物件上執行父型別函式中定義的所有物件初始化的程式碼。結果每個子類例項中都具有了父型別中的屬性以及方法

function Animal(name){
	this.name = name;
	this.colors = ["red","gray"];
}
function Dog(name){
	//繼承了Animal
	Animal.call(this,"mary");//在子型別建構函式的內部呼叫超型別建構函式
	this.color = "gray";
}
Animal.prototype.sayName = function(){
	alert(this.name);
}
 
var dog = new Dog();
var dog1=new Dog();
dog.colors.push("hhh");
console.log(dog.colors);//red gray hhh
console.log(dog1.colors);//red gray
Animal.prototype.swif="hua";
console.log(dog.swif);//undefined
var animal = new Animal();
console.log(animal);   //如果將函式定義在建構函式中,函式複用無從談起
dog.sayName();	 //在超型別的原型中定義的方法,對於子型別而言是無法看到的
複製程式碼

優點

1.解決了1中,子類例項共享父類引用屬性的問題

2.建立子類例項時,可以向父類傳遞引數

3.可以實現多繼承(call多個父類物件)

缺點

1.例項並不是父類的例項,只是子類的例項

2.只能繼承父類的例項屬性和方法,不能繼承父類原型屬性/方法

3.無法實現函式複用,每個子類都有父類例項函式的副本,影響效能

  • 組合函式

組合繼承(combination inheritance),有時候也叫做偽經典繼承,指的是將原型鏈和借用建構函式的 技術組合到一塊,從而發揮二者之長的一種繼承模式。其背後的思路是使用原型鏈實現對原型屬性和方 法的繼承,而通過借用建構函式來實現對例項屬性的繼承。這樣,既通過在原型上定義方法實現了函式 複用,又能夠保證每個例項都有它自己的屬性

function Animal(name){
	this.name = name;
	this.colors = ["red","gray"];
}
function Dog(name){
	//繼承了Animal(屬性)
	Animal.call(this,name);
	this.color = "gray";
}
Animal.prototype.sayName = function(){
	alert(this.name);
}
//繼承方法
Dog.prototype = new Animal();
Dog.prototype.constructor = Animal;
 
var dog = new Dog("2");
dog.colors.push("hhh");
console.log(dog);
var animal = new Animal();
console.log(animal);
dog.sayName();	//可以呼叫
複製程式碼

優點

1.可以繼承例項屬性/方法,也可以繼承原型屬性/方法

2.既是子類的例項,也是父類的例項

3.不存在引用屬性共享問題

4.通過call繼承父類的基本屬性和引用屬性並保留能傳參的優點

5.函式可複用

缺點

1,子類原型上有一份多餘的父類例項屬性,因為父類建構函式被呼叫了兩次,生成了兩份,而子類例項上的那一份遮蔽了子類原型上的。

  • 原型式繼承

  • 寄生式繼承

  • 寄生組合式繼承

相關文章