原型、原型鏈、繼承模式

原型模式

原型模式是js對繼承的一種實現javascript

prototype:構造函數中的屬性,指向該構造函數的原型對象。java

constructor:原型對象中的屬性,指向該原型對象的構造函數函數

_proto_:實例中的屬性,指向new這個實例的構造函數的原型對象this

例子:spa

//Person構造函數
function Person() {
    name = 'Person';
    this.height = '160cm';
}
//在其原型對象中添加age屬性
Person.prototype.age = '18';
//Person的實例p1
var p1 = new Person();
p1.age;//18
p1.name;//undefined
p1.height;//160cm

上圖中原型與構造函數與實例的關係以下:prototype

  • Person爲構造函數
  • Person prototype爲原型
  • p1雖然是經過newPerson得到的,可是p1的原型卻直接指向Person Prototype,而且只獲得了Person中的this的屬性。

** var p1 = new Person()都發生了什麼?code

1.var p1 = new Object(); //此時p1._proto_ = Object Prototype對象

2.p1._proto_ = Person.prototype;繼承

3.Person.call(p1);//使用新對象p1調用函數Person,將this做用域給p1ip

 原型鏈

在定義構造函數的prototype屬性的時候,直接吧一個對象賦值給prototype。

例子:

//Person構造函數
function Person(){
    name = 'Person';
    this.height = '160cm';
}
//原型爲一個Object實例,並有age屬性
Person.prototype = {
    age: '18'
}
//Person的實例p1
var p1 = new Person();
p1.age;//18
p1.name;//undefined
p1.height;//160cm
  • Person Prototype直接變成了Object的一個實例,而且其中再也不有constructor參數
  • Person Prototype直接變成了Object的一個實例,也就是說Person Prototype中有一個_proto_參數,指向Object Prototype

上圖中原型與構造函數與實例的關係以下:

在實例p1中想要調用一個方法或者屬性的時候會沿原型鏈向上查找。

借用構造函數

原型鏈存在的問題,以下:

//父類構造函數
function Super(){
    this.color = ['red','black','blue'];
}
//子類構造函數
function Sub(){}
//子類繼承父類
Sub.prototype = new Super();
//新建一個子類的實例
var ins1 = new Sub();
ins1.color.push('green');

var ins2 = new Sub();
//由於color爲一個引用對象,ins1和ins2的color都指向同一個地址,修改一個就會修改全部實例的color
ins2.color;//'red,belck,blue,green'

防止在原型鏈模式中,全部子類的實例公用一個父類構造函數的引用對象。

使用 借用構造函數 的方式實現繼承。在子類構造函數的內部,調用超類的構造函數構造。以下:

//父類構造函數
function Super(){
    this.color = ['red','black','blue'];
}
//子類構造函數
function Sub(){
    //調用父類構造函數實現繼承
    Super.call(this);
}
//新建一個子類的實例
var ins1 = new Sub();
ins1.color.push('green');
ins1.color;//'red,belck,blue,green'

//在實例ins2上從新執行Super構造函數,從新初始化對象,ins2擁有本身的color屬性
var ins2 = new Sub();
ins2.color;//'red,belck,blue'

繼承

1.組合繼承

結合 原型鏈+借用構造函數 方式,實現組合繼承。

借用構造函數使每一個實例擁有本身的屬性;原型鏈使每一個實例能夠共用方法,實現方法的複用。

//Super中定義屬性name
function Super(name){
    this.name = name;
    this.color = ['red','green'];
}
//Super的原型中定義方法
Super.prototype.sayname = function(){
    console.log(this.name);
}
function Sub(name, age){
    //經過構造函數的方式繼承Super的屬性
    Super.call(this, name);
    //定義本身的屬性
    this.age = age;
}
//經過原型鏈的方式繼承方法
Sub.prototype = new Super();
Sub.prototype.constructor = Sub;

var ins1 = new Sub('ins1',18);

缺點:會調用兩次超類的構造函數,一次在Super.call(this, name); 一次在Sub.prototype = new Super();

致使Sub原型上有屬性name、age,Sub實例上也有屬性name、age。

2.原型式繼承

藉助原型,基於已有的對象建立新對象。

object.Create(參數1,參數2); 參數1用做新對象的原型對象,參數2爲新對象定義額外屬性的對象。

傳入一個參數的狀況:

var person = {
    name: 'person'
}
//基於已有的person對象,建立一個新的anthorp對象
var anthorp = Object.create(person);
//至關於以下語句
function object(o){
    function F(){};
    F.prototype = o;
    return new F();
}
//獲得一個以person爲原型的構造函數的實例
var anthorp = object(person);

傳入兩個參數的狀況:

var person = {
    name: 'person'
}
//基於已有的person對象,建立一個新的anthorp對象,覆蓋以前的name,並新增age
var anthorp = Object.create(person,{
    name: {
        value: 'anthorp'
    },
    age: {
        value: 18
    }
});
anthorp.name; //anthorp

3.寄生繼承

建立一個用於封裝繼承過程的函數,在函數內部加強對象。並返回對象。

talk is cheap, show me code。

//建立一個用於封裝繼承過程的函數,傳入obj
function create(obj){
    //經過調用object方法以obj爲基礎建立一個新對象。此處object表示任何可以返回新對象的函數
    var clone = object(obj);
    //覺得對象新增方法的方式加強對象
    clone.sayHi = function(){
        console.log('hi');
    };
    //返回對象
    return clone;
}

//使用create函數
//一個基礎對象person
var person = {
        name: 'person'
};
//基於基礎對象使用寄生模式生成的新對象
var anthorp = create(person);
anthorp.sayHi();

4.寄生組合式繼承

上面講到組合繼承的缺點:會調用兩次超類的構造函數。

寄生組合式繼承:借用構造函數來繼承屬性,經過原型鏈混成來繼承方法。沒必要在子類原型中調用超類的構造函數。使用寄生繼承來繼承超類的原型,再將結果制定給子類的原型。

//寄生組合式繼承
function inheritProto(Sub, Super){
    //根據Super的原型建立一個新的對象proto
    var proto = object(Super.prototype);
    //加強新對象,爲其賦construtor值
    proto.constructor = Sub;
    //將新對象賦值給子類的原型。
    Sub.prototype = proto;
}
//使用
//Super中定義屬性name
function Super(name){
    this.name = name;
    this.color = ['red','green'];
}
//Super的原型中定義方法
Super.prototype.sayname = function(){
    console.log(this.name);
}
function Sub(name, age){
    //經過構造函數的方式繼承Super的屬性,只在此處調用一次Super構造函數
    Super.call(this, name);
    //定義本身的屬性
    this.age = age;
}
//調用函數,實現繼承。代替以前的Sub.prototype = new Super();語句,防止Super構造函數調用兩次
inheritProto(Sub,Super);
相關文章
相關標籤/搜索