JS--我發現,原來你是這樣的JS:面向對象編程OOP[3]--(JS繼承)

1、面向對象編程(繼承)

這篇博客是面向對象編程的第三篇,JS繼承。繼承顧名思義,就是獲取父輩的各類"財產"(屬性和方法)。html

怎麼實現繼承?

咱們的JavaScript比較特別了,主要經過原型鏈實現繼承的。編程

下面介紹各類實現繼承的方式:原型鏈繼承,借用構造函數,組合繼承,原型式繼承,寄生式繼承,寄生組合式繼承。app

2、實現繼承方式

1.原型鏈方式

原型咱們都知道,每一個構造函數都有一個原型對象(prototype),用於存放共享的屬性方法。
原型鏈繼承原理:咱們要繼承一個父類,那就把這個子類的原型對象指向父類的實例就好了。框架

a.代碼:

//建立父類構造函數
function Father(){
    this.fristName = "Father";
}
//給父類的原型對象添加方法
Father.prototype.getFatherName = function(){
    return this.fristName ;
}

//建立子類構造函數
function Son(){
    this.name = 'jack';
}
//重點這行:把子類的原型對象指向父類實例實現繼承
Son.prototype = new Father();

//咱們還能夠爲子類添加原型方法
Son.prototype.getName = function(){
    return this.name;
}

//測試一下,建立一個子類的實例
var s1 = Son();
s1.getFaterName();  //Father  繼承了爸爸的名字了!

b.一些特別注意的地方:

1.上面咱們看到爲子類原型添加方法getName,它在子類原型對象指向父類實例後,也必定要在這句話後面,若是在前面的話,這方法會被覆蓋的。由於子類原型對象指向父類實例時至關於重寫子類原型對象。
2.爲子類原型添加方法式不能用字面量的方式,這樣會重寫已經繼承類父類的原型對象。函數

c.原型鏈繼承的問題:

很明顯的,繼承的屬性方法都在子類的原型對象裏面,那麼裏面的屬性方法都是共享的,這明顯不是符合咱們日常要求。測試

2.借用構造函數

借?是的,借一下父類的構造函數,怎麼借?那就使用call或者apply方法吧。
借用構造函數原理: 就在子類建立構造函數時內部調用父類的構造函數。優化

代碼演示:

//父類(父類是相對來講的,你們注意一下噢)
function Father(){
    this.colors = ['red','green'];
}
//子類
function Son(){
    //重點這行:內部調用父類構造函數,this是指向將來建立Son的實例
    Father.call(this);
}

//測試一下,繼承了父類的屬性了,而且屬性不會共享噢
var s1 = new Son();
s1.colors.push('blue');
console.log(s1.colors); ['red','green','blue']

//s2實例的colors不會被影響
var s2 = new Son();
console.log(s2.colors); ['red','green']

這裏的借用構造函數能夠實現屬性和方法不會被共享,由於屬性在構造函數中,而不是在原型對象中。可是這也說明這種方式沒辦法共享方法的。this

3.組合繼承(最經常使用)

組合繼承=原型鏈繼承+借用構造函數繼承,沒錯就結合兩種方法就行了。
組合繼承原理:實例共享的屬性方法:我就原型鏈繼承;實例私用的屬性方法:我就借用構造函數(結合二者的優勢)prototype

代碼演示:

//父類
function Father(name){
    this.name = name;
    this.colors = ['red','green'];
}
//父類原型對象
Father.pototype.sayName = function(){
    console.log(this.name);
}
//子類
function Son(name,age){
    //借用父類構造函數
    Father.call(this,name);
    this.age = age;
}
//子類原型指向父類實例,原型鏈繼承
Son.prototype = new Father();
Son.prototype.constructor = Son;   //讓子類的原型只向子類構造函數
Son.prototype.sayAge = function(){
    console.log(this.age);
}

//建立實例對象
var s1 = new Son('Mc ry',22);
var s2 = new Son('yy',20);
//繼承了父類方法,sayName是共享的
s1.sayName(); //mc ry
s2.sayName(); //yy
//繼承colors,每一個實例私有
s1.colors.push('blue');
console.log(s1.colors); ['red','green','blue']
console.log(s2.colors); ['red','green']

可能有的疑惑:在原型鏈繼承那裏,子類原型對象指向類父類的實例,應該繼承了全部的屬性和方法啊,那應該都是共享的,但爲何colors,name屬性不會共享呢?
緣由:在調用借用構造函數時,屬性在子類新實例中建立了,也就是在子類實例中已經有的父類屬性就不用繼續到原型對象中查找了,也就是屏蔽了,因此不會共享了。code

4.原型式繼承

這是道格拉斯·克羅克福提出的一個方式,他提出一個函數object() 來簡單實現不用構造函數的繼承

代碼演示:

//object函數,傳入一個對象
function object(o){
    //建立一個空的構造函數
    function F(){};
    //這個空構造函數的原型對象指向o這個對象
    F.prototype = o ;
    //返回F的實例對象
    return new F();
}

認真讀這段代碼能夠知道,這個函數最終返回一個對象,這個對象擁有傳入對象o的所有屬性和方法。從而實現了繼承。

//一個對象
var person = {
    name : 'ry',
    sayName : function(){
        console.log(this.name);
    }
}

//children對象繼承了person對象全部的屬性方法
var children = object(person);

原型式繼承方式的出現,能夠不用建立構造函數都能繼承另外一個對象的屬性方法。可是很明顯,所用屬性都是共享的。
ES5中有個object.create()方法的做用和上面的object()同樣的。

5.寄生式的繼承

寄生式其實和利用了原型式,都要使用到object函數,但不一樣的是寄生式中新的對象可以添加本身的方法。

function creatAnother(o){
    //調用object繼承o
     var obj = object(o);
     //還能夠添加本身想要的方法
     obj.sayHi = function (){
         console.log('hi');
     }
     //返回這個對象
     retrun obj;
 }
 //新的對象繼承了person
 var anotherPerson = creatAnother(person);
 //繼承後能使用person的方法
 anotherPerson.sayHi(); //"hi"

6.寄生組合式繼承(最理想)

上面組合式方式中雖然是最經常使用的,但有追求的仍是會繼續優化它。由於組合方式中也有不夠好的地方:
一方面:咱們能夠看到調用了兩次父類的構造函數。(一次是原型鏈繼承中子類原型對象指向父類實例時,一次是借用構造函數中)
另外一方面:就是上面疑惑,子類的原型中擁有父類已經有所有屬性,但咱們又要在調用子類構造函數時重寫部分屬性。

因此寄生組合方式就解決了上面,經過一個函數來代替組合方式中的原型鏈繼承。

代碼演示:

//主要是這個函數,實現子類原型對象只繼承父類原型對象中的屬性
function inheritPrototype(subType,superType){
    //利用上面的Object函數,將父類的原型賦予prototype變量
    var prototype = object(superType.prototype);
    //將prototype的構造函數從新指向子類
    prototype.constructor = subType;
    //將prototype給sub的原型對象
    subType.prototype = prototype;
}

//將前面的組合繼承改寫
//父類
function Father(name){
    this.name = name;
    this.colors = ['red','green'];
}
//父類原型對象
Father.pototype.sayName = function(){
    console.log(this.name);
}
//子類
function Son(name,age){
    //借用父類構造函數
    Father.call(this,name);
    this.age = age;
}
//(這行用inheritPrototype函數替換)子類原型指向父類實例,原型鏈繼承
inheritPrototype(Son, Father);
Son.prototype.sayAge = function(){
    console.log(this.age);
}

寄生組合式的繼承方式是最理想的方式,它使得子類構造函數繼承父類構造函數的屬性,子類的原型對象繼承父類原型對象的屬性和方法。

3、小結

1.此次博客講述了在js中是如何實現繼承的,有不少中方式,但主要的是組合方式寄生組合方式。繼承後咱們可以使用父類的屬性和方法,增長了代碼的重用性。
2.瞭解js繼承做用:有助於咱們去閱讀一些框架的源碼,可能本次代碼有點難以理解,我打上了大量的註釋,供你們一塊兒閱讀,仍是那句話,多看幾遍,其義自見。若是以爲有收穫,就點個贊吧,關注我吧。

  • 細心的朋友可能發現,我把文章的樣式修改了,相似Markdown風格咯,看起來比較清爽,舒服。

同系列幾篇:
第一篇:JavaScript--我發現,原來你是這樣的JS(一)(初識)
JS--我發現,原來你是這樣的JS:面向對象編程OOP[1]--(理解對象和對象屬性類型)
JS--我發現,原來你是這樣的JS:面向對象編程OOP[2]--(建立你的那個對象吧)
我發現,原來你是這樣的JS所有文章彙總(點擊此處)

本文出自博客園:http://www.cnblogs.com/Ry-yuan/ 做者:Ry(淵源遠願) 歡迎轉載,轉載請標明出處,保留該字段。

相關文章
相關標籤/搜索