基於原型,原型鏈的繼承許多方法及利弊

沒有大白話,有的只是簡短而有道理的知識。皮一下,很開心,嘿嘿嘿嘿。

//什末是原型?

原型:一個函數是一個類也是一個對象還是一個方法,只要是函數就會有它自己的prototype屬性,這個屬性實質是一個指針,指向函數的原型對象,我們把構造函數的原型對象稱爲對象實例的原型。

函數.prototype = 實例對象.__proto__

構造函數的原型就是原型對象,每一個函數都具有 prototype屬性

原型對象.constructor=函數本身

//什末是原型鏈?

原型鏈:一個函數的實例對象指向另一個函數的原型對象,那末這個實例對象就會擁有另一個函數的原型上所有的屬性和方法。以此類推,就會形成一個鏈。有點難理解啊,嘿嘿吧。借用一個例子說明白

    A.prototype={//原型
          constructor:A,
          name:'liming',
          say:function(){
          console.log(1);
        } 
     	}
     function  A(){ 
          this.sex='hhhh'
     }    
     function B(){ }
     var b=new B();
     b.__proto__=A.prototype;
      console.log( b.say,b.name,b.sex);
////////////////////////////////////////////////////結果
ƒ (){
          console.log(1);
        } 
"liming" 
undefined

由此看出實例對象b可以訪問A的原型上的屬性和方法。(但是拿不到構造函數A自身的屬性和方法)下面介紹解決方法,這樣就形成一個鏈,以此類推,c.__proto__=B.prototype....................

形成一堆堆的鏈。原型鏈的頂端爲null。

好了,重頭戲來了,本文目的就是繼承,前期鋪墊很多,後面就好理解,繼承它有多種實現方法,下面就一一列舉它們的利弊

一: 傳統形式-->原型鏈

Grand.prototype.lastName = 'DU';
function Grand(){
}
var grand = new Grand();
Father.prototype = grand;
function Father(){
}
var father = new Father();
Son.prototype = father;
function Son(){

}
var son = new Son();
console.log(son.lastName)//DU


        缺點:過多的調用了沒用的屬性,son會繼承Father的全部屬性還會繼承Grand的全部屬性,並且如果在Son實例化兩個對象(son1,son2)時,改變一個實例化對象(son1)的屬性時另一個實例化對象(son2)也會發生改變。

二:借用構造方法

 function Person(name,age,sex){
	this.name = 'du';
	this.age = age;
        this.sex = sex;
}
function Student(name,age,sex,grade){
         Person.apply(this,arguments);//用apply這個方法前提是這個方法得是new 出來的
         //此時的this的原型還是Student.prototype的
         this.grade = grade;
	 }
         var student = new Student('liming',18,'女');
         console.log(student);
        //輸出結果:
        Student {name: "du", 
                age: 18, 
                sex: "女",
                grade: undefined
            }

缺點是:借用構造方法但只能借用別人的方法不能借用別人的原型,不能繼承到原型鏈上的屬性和方法。
              

三:

Father.prototype.lastName = 'DU';
	function Father(){
	}
	function Son(age){
             this.age = age;
	}
	function inherit(Target,Origin){//傳兩構造函數
             Target.prototype = Origin.prototype;
	}
	inherit(Son,Father);//先繼承
	var son = new Son();
	var father = new Father();
	Son.prototype.sex = 'nv';
        console.log(son.lastName,son.sex,father.lastName,father.sex)// DU nv DU nv

缺點:由此得子類繼承了父類的屬性但子類的私有屬性也被父類拿到了,爲了改進這一問題引出來下面的方法

四:完美模式

function inherit(Target,Origin){//傳兩構造函數
	function F(){};
	F.prototype = Origin.prototype;
	Target.prototype = new F();//接用F()鏈接一個橋樑
	Target.prototype.constructor = Target;//將son的指向歸位
	Target.prototype.uber =  Origin.prototype;//現在Target是繼承 new F()但這不是它真正繼承的父類而正真繼承的父類就是Origin.prototype
	}
	Father.prototype.lastName = 'DU';
	function Father(){
	}
	function Son(){
	}
	inherit(Son,Father);
	Son.prototype.sex = 'nv';
	var son = new Son() ;
	var father = new  Father();
         console.log(son.lastName,son.sex,father.lastName,father.sex);//DU nv DU undefined
         

這種方式是將父類的prototype傳給一個“乾淨的函數”的原型,再將這個“乾淨的函數”的原型傳給子類的原型,巧妙的用了一個函數進行過渡,這樣下來子是子,父是父,間接性的形成繼承,但子類在根本上還是繼承了父類

值得注意的是在沒有加Target.prototype.constructor = Target;這句話時

console.log(son.constructor);     //Father()

子類的constructor本應是它函數本身,然而這裏打印的卻Father()

是因爲在底部實現了下面的代碼:

son.__proto__->new F()                                      //子類實例化的對象指向了“乾淨的函數”的實例
new F().__proto__->Father.prototype                 //“乾淨的函數”的實例對象指向父類的原型
Father.prototype.constructor ->Father                //父類原型的構造器就是父類它自己

所以最後打印出來的就是Father()

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章