js 中繼承方式小談
阿新 • • 發佈:2018-11-02
題外話
前段時間面試中筆試題有這道題目:
請實現一個繼承鏈,要求如下:
建構函式
A()
:建構函式中有consoleA
方法,可以實現console.log("a")
例項物件 a:a 可以呼叫consoleA
方法建構函式
B()
:建構函式中有consoleB
方法,可以實現console.log("b")
例項物件 b:b 可以呼叫consoleA
和consoleB
方法建構函式
C()
:建構函式中有consoleC
方法,可以實現console.log("c")
例項物件 c:c 可以呼叫consoleA
和consoleB
和consoleB
方法
ok,這個題目暫時擱置,再回到這個問題之前,我們先來看看 js 的繼承方式
約定
// 父類
function Super() {
this.name = "parent0";
this.colors = ["red", "blue", "yellow"];
}
Super.prototype.sex = "男";
Super.prototype.say = function() {
console.log(" Oh,My God! ");
};
構造繼承
原理
通過使用 call、apply 方法可以在新建立的物件上執行建構函式,用父類的建構函式來增加子類的例項
實現
function Sub() { Super.call(this); this.type = "sub"; } var s = new Sub(); console.log(s.colors); //[ 'red', 'blue', 'yellow' ] s.say(); //報錯 sub.say is not a function
優缺點
- 優點:簡單明瞭,直接繼承超類建構函式的屬性和方法
- 缺點:無法繼承原型鏈上的屬性和方法
原型鏈式繼承(借用原型鏈實現繼承)
實現
function Sub() { this.type = "sub"; } Sub.prototype = new Super(); var s1 = new Sub(); var s2 = new Sub(); sub1.colors.push("black"); console.log(s1.colors); //[ 'red', 'blue', 'yellow', 'black' ] console.log(s2.colors); //[ 'red', 'blue', 'yellow', 'black' ] sub1.say();
我們例項化了兩個 Sub,在例項 s1 中為父類的 colors 屬性 push 了一個顏色,但是 s2 也被跟著改變了。造成這種現象的原因就是原型鏈上中的原型物件它倆是共用的。這不是我們想要的,s1 和 s2 這個兩個物件應該是隔離的,
組合繼承
這裡所謂的組合是指組合借用建構函式和原型鏈繼承兩種方式。
function Sub() {
Super.call(this);
this.type = "sub";
}
Sub.prototype = new Super();
var s1 = new Sub();
var s2 = new Sub();
s1.colors.push("black");
console.log(s1.colors); //[ 'red', 'blue', 'yellow', 'black' ]
console.log(s2.colors); //[ 'red', 'blue', 'yellow' ]
可以看到,s2和s1兩個例項物件已經被隔離了,但這種方式仍有缺點。父類的建構函式被執行了兩次,第一次是Sub.prototype = new Super(),第二次是在例項化的時候,這是沒有必要的。那我們就繼續優化吧!!
組合式繼承優化1
function Sub(){
Super.call(this)
this.type = "sub";
}
Sub.prototype=Super.prototype
var s1=new Sub()
var s2=new Sub()
但是請看以下程式碼
console.log(s1.constructor.name);//Super
我們還可以用.constructor來觀察物件是不是某個類的例項:從這裡可以看到,s1的建構函式居然是父類Super,而不是子類sub,這顯然不是我們想要的。
組合式繼承優化2
function Sub(){
Super.call(this)
this.type = "sub";
}
Sub.prototype=Super.prototype
Sub.constructor=Sub
var s1=new Sub()
var s2=new Sub()
console.log(s1 instanceof Sub);//true
console.log(s1 instanceof Super);//true
console.log(s1.constructor.name);//Super
perfect!!
開題解答
回到開頭的問題中來,
function A(){}
A.prototype.consoleA=function(){
console.log("a")
}
function B(){
A.call(this)
}
B.prototype.consoleB=function(){
console.log("b")
}
Object.assign(B.prototype,A.prototype)
function C(){
B.call(this)
}
C.prototype.consoleC=function(){
console.log("c")
}
Object.assign(C.prototype,B.prototype)