經典原型鏈、繼承解析

原型

咱們知道任何一個函數都存在一個prototype屬性,他是個對象,這個對象 咱們就叫他原型對象
這個原型對象自己也自帶兩個屬性:constructor 和 protoapp

constructor: 這個屬性是指向建立此對象的構造函數的引用,構造函數的實例化對象,也能夠經過constuctor屬性來訪問構造它的那個函數

_proto_: 這個屬性指向建立此對象的構造函數的prototype原型對象的引用函數

例子:
//個人家族姓莫
function Parent(){
    this.name = 'mo'
}
//我家族是音樂世家
 Parent.prototype.work = function(){
     return 'musicing'
 }
 
 //爸媽生了我
 var me = new Parent()

 //我也要唱歌
 console.log(me.work())   //=>musicing

 //爸爸媽媽又生了二胎
 var myBrother = new Parent()

 //他也會唱歌
 console.log(myBrother.work())  //=>musicing

 //證實我兩是不是親生的
 console.log(me.work() === myBrother.work())  //=>true

解釋一波:
    me 和 myBrother 是構造函數Parent()new出來的的一個實例,me 和 myBrother 都有一個隱式屬性_proto_,引用Parent() 的prototype屬性來獲得繼承

原型鏈

在訪問 me 的 work 方法時,找不到, 就會順着_protp_屬性往上在構造函數的prototype找,找到了就中止,沒找到繼續往上到Object.prototype找,再沒找到就到null了,天然也找不到就只能返回undifined,這種鏈式的引用就是原型鏈,經過原型鏈實現繼承仍是很方便的this

萬一原型鏈斷鏈呢?

緣由:若是對 構造函數 或者 原型鏈 修改一些方法或者屬性的時候,致使函數的constructor不等於建立它的構造函數,那就會斷鏈prototype

若是 先實例再經過 字面量添加或修改,那麼後新定義的方法就對先繼承的方法或屬性就會不在生效,就會斷鏈,這是由於字面量來修改原型時,constructor發生了改變,也就是說該函數指向的建立該函數的構造函數發生了改變,字面量默認的constructor的值是Object(),因此爲了不斷鏈,儘可能不要使用字面量從新賦值,修改code

例子:
 //建立一個構造函數Car
function Car(){
 this.brand = '大奔';
}
//大奔 80萬
Car.prototype.price = '80';

//我來買一個大奔 先實例
var benCar1 = new Car();
console.log(benCar1.price)  //=>80

//我但願個人大奔 帶有翅膀的 能飛
Car.prototype = {
 hasWing: true,
 hasFlying: function(){
     console.log('flying...')
 }
}

var benCar = new Car()

(1) 正常狀況下 
console.log(benCar1.brand,benCar1.price,benCar1.hasWing,benCar1.hasFlying,benCar1.constructor)
 //=> 大奔 80 undefined undefined  ƒ Car(){}

(2)字面量添加屬性方法時
console.log(benCar.brand,benCar.price,benCar.hasWing,benCar.hasFlying,benCar.constructor) 
//=> 大奔 undefined true ƒ (){} ƒ Object() { [native code] }

繼承的幾種方式

(1) 原型鏈繼承對象

原型鏈繼承是經過 new實例化構造函數 賦給子類的原型, 其實實例的子類自己是徹底的空對象,全部的屬性方法都須要去原型鏈上找。
例子:

function Grandpa(){
    this.name = 'mo'
}
Grandpa.prototype.work = function(){
     return 'musicing'
 }
 function Parent(){

}
Parent.prototype = new Grandpa()

 var me = new Parent()
 console.log(me.work())   //=>musicing 我找啊找啊原來是Grandpa會musicing
 var myBrother = new Parent()
 console.log(myBrother.work())  //=>musicing

 console.log(me.work() === myBrother.work())  //=>true

(2) 構造函數繼承繼承

構造函數繼承 經過apply去調用父類的構造函數,達到繼承父類的實例屬性,對,只能繼承屬性,要想繼承方法 採用寄生組合繼承
例子
function Grandpa(firstname){
    this.name = 'mo ' + firstname
}
Grandpa.prototype.work = function(){
     return 'musicing'
 }
 function Parent(firstname){
    Grandpa.apply(this, arguments)
}
Parent.prototype = new Grandpa()

 var me = new Parent('alice')
 console.log(me.work())   //=>musicing
 var myBrother = new Parent('bob')
 console.log(myBrother.work())  //=>musicing

 console.log(me.work() === myBrother.work())  //=>true
 console.log(me.name, myBrother.name,me.name === myBrother.name)//=>mo alice ,mo bob ,false

(3) 寄生組合繼承內存

寄生組合繼承是咱們常常要用到的,組合了原型和構造函數,結合Object.create(obj),方法對傳入的對象進行淺拷貝,這樣能夠實現對實例屬性和原型屬性分別進行繼承

淺拷貝:僅僅是指向被拷貝的內存地址,若是原地址中對象被改變了,那麼淺拷貝出來的對象也會相應改變ci

例子:
// 寄生組合繼承
function Grandpa(firstname){
    this.name = 'mo ' + firstname
}
Grandpa.prototype.work = function(){
     return 'musicing'
 }
 function Parent(firstname){
    Grandpa.apply(this, arguments)
}
// Parent.prototype = new Grandpa()
//改爲
Parent.prototype = Object.create(Grandpa.prototype); // Object.create()將父級對象的屬性和方法進行引用
Parent.prototype.constructor = Parent;  //將該函數的construnctor指向parent構造函數
console.log(Parent.prototype)

 var me = new Parent('alice')
 var myBrother = new Parent('bob')

 console.log(me.work() === myBrother.work())  //=>true
console.log(me.name, myBrother.name,me.name === myBrother.name)//=>mo alice ,mo bob ,false

好了,有時間還會補充...原型鏈

相關文章
相關標籤/搜索