讀JavaScript繼承有感

繼承

本文是在讀了JavaScript設計模式的繼承部分做的總結

類式繼承
function Father(firstName){
    this.firstName = firstName;
    this.family = ["father", "mother", "son"];
}

Father.prototype.showFirstName = function(){
    return this.firstName;
}

function Child(sex){
    this.sex = sex;
}

Child.prototype = new Father("ccc");

var me = new Child("male");
console.log(me.showFirstName());

類式繼承最重要的一步是將父類的實例賦值給子類的prototype對象
實例化的父類包含了父類的共有屬性和方法,然後這個實例化的對象的 _ proto_ 屬性又指向了父類的prototype對象,所以這個實例對象能訪問父類原型上的屬性和方法
當把這個實例化的對象賦值給子類的prototype屬性時,子類的原型也就能訪問到父類的原型上的屬性和方法了
用子類實例化的對象能訪問到子類的原型,所以進而能訪問到父類的原型

me.__proto__ === Child.prototype;
Child.prototype.__proto__ === Father.prototype;

me.__proto__.__proto__ === Father.prototype;

類式繼承有下面兩個問題:

  • 如果父類裏的公有屬性有引用類型的,實例化子類後,這些屬性的修改會相互影響
  • 實例化子類的時候無法向父類傳遞參數並初始化父類裏的公有屬性
var son = new Child("male"),
    daugter = new Child("famale");
son.family.push("daugter");
console.log(son.family);
console.log(daugter.family); // ["father", "mother", "son", "daugter"]
//daugter實例對象的family屬性也被修改了
構造函數繼承
function Father(firstName, family){
    this.firstName = firstName;
    this.family = family;
    this.showFirstName = function(){
        return this.firstName;
    }
}

function Child(sex, firstName, family){
    this.sex = sex;
    Father.call(this, firstName, family);
}

var son = new Child("male", "c", ["father", "mother", "son"]);
console.log(son);

構造函數繼承的重點在於,Father.call(this, “”, “”),用子類的this對象去調用父類的構造函數,這樣就能在子類的實例化的對象上覆制綁定父類的公有屬性和方法,互不影響,但是對於本應共享的方法也都複製了一份,不能共用

組合繼承
function Father(firstName){
    this.firstName = firstName;
    this.family = ["father", "mother"];
}

Father.prototype.showFirstName = function(){
    return this.firstName;
}

function Child(sex, firstName){
    this.sex = sex;
    Father.call(this, firstName);
}

Child.prototype = new Father();

var son = new Child("male", "c");
console.log(son.showFirstName()); // "c"

組合繼承的過程中,父類的構造函數被執行了兩遍,會導致子類實例化的對象的原型鏈上有重名的屬性,當訪問時,子類的屬性會覆蓋原型鏈上重名的屬性

寄生式繼承
function inherit(father){
    function F(){}
    F.prototype = father;
    return new F();
    //這麼做的目的就是得到一個乾淨的實例,沒有父類裏的公有屬性,只保留了父類原型上的屬性和方法
}

var family = {
    count: 3,
    person: ["father", "mother",  "son"]
}

function createFamily(obj){
    var o = inherit(obj);
    o.getCount = function(){
        return this.count;
    }
    return o;
}

var test = createFamily(family);
console.log(test);
寄生組合式繼承
function inherit(father){
    function F(){}
    F.prototype = father;
    return new F();
}

function inheritPrototype(child, father){
    var p = inherit(father.prototype);
    p.constructor = child;
    //這裏的p,感覺更像是個過度,一個乾淨的繼承了父類原型的對象,然後其constructor屬性被更正到了子類上,整個過程變得更加清晰,層次更清楚
    child.prototype = p;
}

function Father(firstName){
    this.firstName = firstName;
    this.family = ["father", "mother"];
}

Father.prototype.showFirstName = function(){
    return this.firstName;
}

function Child(sex, firstName){
    this.sex = sex;
    Father.call(this, firstName);
}

inheritPrototype(Child, Father);

Child.prototype.showSex = function(){
    return this.sex;
}

var son = new Child("male", "c");
console.log(son);

上面關於寄生組合繼承實現方式,無意中發現這種方式也許更好

function Father(firstName){
    this.firstName = firstName;
    this.family = ["father", "mother"];
}

Father.prototype.showFirstName = function(){
    return this.firstName;
}

function Child(sex, firstName){
    this.sex = sex;
    Father.call(this, firstName);
}

Object.setPrototypeOf(Child.prototype, Father.prototype);

Child.prototype.showSex = function(){
    return this.sex;
}

var son = new Child("male", "c");
console.log(son);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章