一、原型繼承
原型繼承是Javascript中最基本也是最核心的繼承方式,如何實現原型鏈的繼承呢?由於Javascript中所有的對象都擁有原型屬性(即__proto__屬性),該屬性指向父對象,實現對象之間的繼承可以通過指定某個對象(即子對象)的原型__proto__為其父對象。在實踐中我們可以使用如下方式來實現原型繼承:
function Parent(name){
this.name = name;
this.sayHello = function(){
alert('hello ' + this.name);
}
}
Parent.prototype.colors = ['blue', 'black', 'yellow'];
function Child(name){
this.name = name;
}
// 指定Child的原型為Parent
Child.prototype = new Parent();
Child.prototype.constructor = Child;
var child1 = new Child('child1');
var child2 = new Child('child2');
child1.sayHello(); // hello child1
child2.sayHello(); // hello child2
alert(child1.colors); // blue, black, yellow
child1.colors.push('red');
alert(child2.colors); // blue, black, yellow, red
在上述代碼中,我們聲明了一個Parent函數和一個Child函數,Parent 函數中有 name 屬性和 sayHello 方法,Child 函數中只有 name 屬性,我們實現了 Child 的原型繼承自 Parent,並把 Child的原型的構造器設置為 Child自己,實例化 Child 後就可以訪問到 Parent的屬性和方法。
實踐中請注意:
- 實現原型繼承時,需要在既定的基礎上對其進行開發,建議使用封裝性較好的函數進行開發,方便以後在其他項目也可以復用。
- 你也可以通過Object.create函數進行原型繼承,這個函數可以上述代碼整合成如下:
function object(p) {
function F() {}
F.prototype = p;
return new F();
}
function Parent(name){
this.name = name;
this.sayHello = function(){
alert('hello ' + this.name);
}
}
Parent.prototype.colors = ['blue', 'black', 'yellow'];
function Child(name){
this.name = name;
}
// 指定Child的原型為Parent
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
var child1 = new Child('child1');
var child2 = new Child('child2');
child1.sayHello(); // hello child1
child2.sayHello(); // hello child2
alert(child1.colors); // blue, black, yellow
child1.colors.push('red');
alert(child2.colors); // blue, black, yellow, red
二、經典繼承
經典繼承,即利用 call 或者 apply 方法來實現繼承。經典繼承相對於原型繼承來說,顯得更加靈活、高效。在設計模式中,很多模式基於經典繼承的思想來構建,這些設計模式中都在使用 call 和 apply 方法來實現調用父類構造函數。在實踐中我們可以使用如下方式來實現經典繼承:
function Parent(name){
this.name = name;
this.sayHello = function(){
alert('hello ' + this.name);
}
}
Parent.prototype.colors = ['blue', 'black', 'yellow'];
function Child(name){
// 將 Parent 的作用域賦給 Child,以實現父類實例化
Parent.call(this, name);
this.age = 18;
}
var child = new Child('child');
alert(child.name); // child
alert(child.age); // 18
alert(child.colors); // undefined
我們聲明了兩個函數,Parent 函數中有 name 屬性和 sayHello 方法,Child 函數中通過使用 Parent.call() 將 Parent 函數的作用域賦給了 Child,從而實現了 Child 對象繼承 Parent 對象的屬性和方法。這種方式不會污染父類的原型屬性,同時也可以在子類實例化時傳入屬性,實際開發中應用廣泛。
實踐中請注意:
- 經典繼承可以實現單繼承或者多重繼承,如下例中既同時繼承自Father和Mother:
function Father() {
this.name = 'father';
}
function Mother() {
this.age = 30;
}
function Child() {
Father.call(this);
Mother.call(this);
}
var child = new Child();
console.log(child.name); // father
console.log(child.age); // 30
三、組合繼承
組合繼承,即將原型繼承和經典繼承進行組合來實現繼承。通過使用原型鏈實現對超類屬性(即方法)的共享,在通過調用父類構造函數來實現 prototype 中屬性的定義。在實踐中我們可以使用如下方式來實現組合繼承:
function Parent(name){
this.name = name;
this.sayHello = function(){
alert('hello ' + this.name);
}
}
Parent.prototype.colors = ['blue', 'black', 'yellow'];
function Child(name){
Parent.call(this, name);
}
Child.prototype = new Parent();
Child.prototype.constructor = Child;
var child1 = new Child('child1');
var child2 = new Child('child2');
child1.sayHello(); // hello child1
child2.sayHello(); // hello child2
alert(child1.colors); // blue, black, yellow
child1.colors.push('red');
alert(child2.colors); // blue, black, yellow
我們聲明了兩個函數,Parent 函數中有 name 屬性和 sayHello 方法,Child 函數中在調用 Parent.call(this, name) 的情況下實現經典繼承,然後在原型鏈上繼承了 Parent 的屬性和方法,最後將 Child 的構造函數重新賦值給 Child 的原型構造函數,從而實現 Child 對象的屬性和方法的繼承。關於組合繼承,在實踐中是一種非常好的方式。
實踐中請注意:
- 在編寫組合繼承時,需要注意不要重複實例化父對象,我們可以使用 Object.create(Parent.prototype) 的方式來創建一個中介者。
function Parent(name){
this.name = name;
this.sayHello = function(){
alert('hello ' + this.name);
}
}
Parent.prototype.colors = ['blue', 'black', 'yellow'];
function Child(name){
Parent.call(this, name);
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
var child1 = new Child('child1');
var child2 = new Child('child2');
child1.sayHello(); // hello child1
child2.sayHello(); // hello child2
alert(child1.colors); // blue, black, yellow
child1.colors.push('red');
alert(child2.colors); // blue, black, yellow
四、寄生組合繼承
寄生組合繼承,即相對於組合繼承來說不會重複實例化父對象,而是創建一個空的函數作為中介者來實現繼承。在實踐中我們可以使用如下方式來實現寄生組合繼承:
function Parent(name){
this.name = name;
this.sayHello = function(){
alert('hello ' + this.name);
}
}
Parent.prototype.colors = ['blue', 'black', 'yellow'];
function Child(name){
Parent.call(this, name);
}
var __ = function() {};
__.prototype = Parent.prototype;
Child.prototype = new __();
Child.prototype.constructor = Child;
var child1 = new Child('child1');
var child2 = new Child('child2');
child1.sayHello(); // hello child1
child2.sayHello(); // hello child2
alert(child1.colors); // blue, black, yellow
child1.colors.push('red');
alert(child2.colors); // blue, black, yellow
在上述代碼中,我們通過創建名( __ )叫佔位符(thunk)的函數作為中介者來實現繼承,並將 Child 的原型指向這個中介器,避免了在使用 Object.create() 方法的重複實例化問題。實踐證明在大多數情況下,寄生組合繼承是一種更好的繼承方式,我們還可以通過Module模式對寄生組合繼承進行封裝,易於復用。
實踐中請注意:
- 當某個父類中有多個子類時,我們可以將父類中公共的方法提取出來,在Child構造函數的裡面進行調用,達到代碼復用的目的。
function Parent(name){
this.name = name;
this.sayHello = function(){
alert('hello ' + this.name);
}
}
Parent.prototype.colors = ['blue', 'black', 'yellow'];
function Child(name){
Parent.call(this, name);
}
function extend(child, parent){
var Middle = function(){};
Middle.prototype = parent.prototype;
child.prototype = new Middle();
child.prototype.constructor = child;
child.prototype.parent = parent.prototype;
return child;
}
extend(Child, Parent);
var child1 = new Child('child1');
var child2 = new Child('child2');
child1.sayHello(); // hello child1
child2.sayHello(); // hello child2
alert(child1.colors); // blue, black, yellow
child1.colors.push('red');
alert(child2.colors); // blue, black, yellow
五、ES6 繼承
ES6 提出了 extends 關鍵字,可以極大的簡化繼承的操作,我們只需要在子類中添加 extends 關鍵字,並在解析 super 關鍵字時使用 new 關鍵字調用父類的構造函數即可。在實踐中我們可以使用如下方式來實現ES6繼承:
class Parent{
constructor(name){
this.name = name;
}
sayHello(){
alert('hello ' + this.name);
}
}
Parent.prototype.colors = ['blue', 'black', 'yellow'];
class Child extends Parent{
constructor(name){
super(name)
}
}
let child1 = new Child('child1');
let child2 = new Child('child2');
child1.sayHello(); // hello child1
child2.sayHello(); // hello child2
alert(child1.colors); // blue, black, yellow
child1.colors.push('red');
alert(child2.colors); // blue, black, yellow
在上述代碼中,我們聲明了一個 Parent 類和一個 Child 類,Child 類直接繼承自 Parent 類,我們只需要在 Child 類中添加 extends 關鍵字,並在解析 super 關鍵字時使用 new 關鍵字調用父類的構造函數即可,如果是需要繼承父對象的屬性和方法,則直接使用 super 關鍵字即可。ES6 繼承更加直觀舒適,使用起來也非常方便。
實踐中請注意:
- ES6繼承在編碼時需要注意瀏覽器的兼容性問題,如果是在較低版本的瀏覽器中使用的話,需要通過 babel 來實現ES6的編譯。
- 可以在ES6中通過 Symbol.hasInstance 重
原創文章,作者:QJQOE,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/369384.html