對于javascript中的繼承,因為js中沒有后端語言中的類式繼承。所以js中的繼承,一般都是原型繼承(prototype)。
function P (name){this.name = name;this.say = function(){console.log('p');}
}function S (name,id){this.id = id;this.eat = function(){console.log('s');}
}S.prototype = P.prototype;var s = new S();
以上代代碼存在一個重要問題:
S.prototype = P.prototype;
該句代碼會導致,在改動子類的prototype的時候,相同會影響父類的prototype元素。
解決方案一般採用new 父級對象的方式:
S.prototype = new P();
這里涉及到new 的工作原理,new P(), 會依據P的prototype對象建立一個實例,然后將構造方法中的成員變量和成員方法設置進去。
此種方式, 改動子類的prototype則不會影響父類的prototpye(因為是新建了一個對象,不再是指向同一個prototype).
設置prototype之后。還須要重置子類S的contrustor,讓其重指向 S。而不是new P() 所相應的construstor. 例如以下:
S.prototype.constructor = S;
非常多時候這里不重置。不會出現故障,可是假設后面須要用到constructor 來創建實例,或者判定實例類型,則會出現錯誤。
所以這里最好是重置。
上面所述的原型繼承。盡管能夠通過JavaScript原型繼承,可是相同存在不足。
此種繼承方式。不可以繼承在構造方法設置的成員變量和成員方法。僅僅能繼承在原型中設置的方法,或者屬性。
如:
var s = new S('yang','01');
當中的第一個參數,并不能傳遞給到name屬性。 于是引出另外一種繼承方式: 對象冒充。
function P (name){this.name = name;this.say = function(){console.log('p');}
}function S (name,id){P.call(this,name);this.id = id;this.eat = function(){console.log('s');}
}var s = new S('yanghi','test');
在子類構造方法中,借用父類的構造方法。使子類具有父類的屬性和方法。
例如以下:
P.call(this,name);
該句代碼與例如以下效果一樣:
this.name = name;
this.say = function(){}
從而將父類的屬性復制到子類中。從而實現對象冒充。
那么該此種對象冒充方式。相同存在問題:
1 無法繼承原型prototype中的屬性和主法。
2 構造方法的成員方法在父類和子類中都會有一份拷貝,造成內存的添加。
所以最好的實現方式是:
1 對原型對象。採用原型繼承
2 構造方法中的屬性和方法。採用對象冒充。
這也是當前絕大對數的js繼承庫所採用的實現方式。例如以下:
function P (name){this.name = name;
}P.prototype.say = function(){console.log('say');
}function S (name,id){P.call(this,name);this.id = id;
}function Bare(){}
Bare.prototype = P.prototype;
S.prototype = new Bare();
S.prototype.constructor = S;S.prototype.eat = function(){console.log('eat');
}var s = new S('yanghi','test');
這里的成員屬性。採用對象冒充,成員方法採用原型繼承。
注意一點。這里實現原型繼承須要採用一個中間變量。例如以下:
S.prototype = new Bare();
假設不採用中間變量。直接new P() 的話,會出現故障。
因為new 會依照P的prototype對象模版,創建一個對象,這一步沒有問題。
可是接下來。它會將P構造方法的成員屬性也設置到這個對象中,就會導致對這個對象污染。
這里我們僅僅須要它的prototype就能夠了。其他的成員變量採用對象冒充的方式就能夠了。
第三種實現繼承方式,採用ES5 Object.create實現。
//Shape - superclass
function Shape() {this.x = 0;this.y = 0;
}Shape.prototype.move = function(x, y) {this.x += x;this.y += y;console.info("Shape moved.");
};// Rectangle - subclass
function Rectangle() {Shape.call(this); //call super constructor.
}Rectangle.prototype = Object.create(Shape.prototype);var rect = new Rectangle();rect instanceof Rectangle //true.
rect instanceof Shape //true.rect.move(1, 1); //Outputs, "Shape moved."
只是此方式最大的問題還是兼容性,須要 IE 9 + , safari 5 +,opera 11.6 +?
以上就是對象繼承的一些問題,以記錄之,謹防忘記。