繼承有類式繼承,構造函數繼承人,組合繼承
1:類式繼承:
// 聲明父類
function Parent(){ this.parentValue = true; }
// 為父類添加共有方法
Parent.prototype.getParentValue = function(){ return this.parentValue; }
// 聲明子類
function Child(){ this.childValue = false; }
// 繼承父類
Child.prototype = new Parent();
// 為子類添加共有方法
Child.prototype.getChildValue = function(){ return this.childValue; }
類的原型對象的作用是為類的原型添加共有屬性和方法,
但類必須通過原型prototype來訪問這些屬性和方法。
當實例化一個父類時,
新建對象復制了父類構造函數內的屬性和方法,並且將原型__proto__
指向了父類的原型對象,
這樣就擁有了父類原型對象上的屬性和方法,新建對象可以直接訪問父類原型對象的屬性和方法,
接著將這個新建的對象賦值給子類的原型,那麽子類的原型就可以訪問父類的原型屬性和方法。
將這個對象賦值給子類的原型,那麽這個子類就可以訪問父類原型上的屬性和方法,並且可以訪問從父類構造函數中復制的屬性和方法。我們可以來測試一下:
var child = new Child();
console.log(child.getParentValue()); // true
console.log(child.getChildValue()); // false
console.log(child instanceof Parent); // true
console.log(child instanceof Child); // true
console.log(Child instanceof Parent); // false
但這種繼承方式有2個缺點:
-
由於子類是通過其原型prototype對父類實例化,如果父類中的共有屬性是引用類型,會被所有實例所共享,一個子類的實例修改了該屬性會直接影響到所有實例。例如:
function Parent(){ this.values = [‘A‘,‘B‘,‘C‘]; } function Child(){} Child.prototype = new Parent(); var child1 = new Child(); var child2 = new Child(); console.log(child2.values); // ["A","B","C"] child1.values.push(‘D‘); console.log(child2.values); // ["A","B","C","D"]
-
創建父類實例時,是無法向父類傳遞參數的,也就是無法對父類構造函數內的屬性進行初始化。例如這種錯誤的繼承方式:
function Parent(name){ this.name = name; } function Child(){} Child.prototype = new Parent(‘name‘); // 錯誤
構造函數繼承
// 聲明父類
function Parent(name){
this.name = name;
this.values = [‘A‘,‘B‘,‘C‘];
}
Parent.prototype.showName = function(){
console.log(this.name);
}
// 聲明子類
function Child(name){
Parent.call(this,name);
}
var child1 = new Child(‘one‘);
var child2 = new Child(‘two‘);
child1.values.push(‘D‘);
console.log(child1.name); // one
console.log(child1.values); // ["A","B","C","D"]
console.log(child2.name); // two
console.log(child2.values); // ["A","B","C"]
child1.showName(); // TypeError
語句Parent.call(this,name);
是構造函數繼承的精華,call
方法可以更改函數的作用環境,在子類中執行該方法相當於將子類的變量在父類中執行一遍,此時父類方法中的this
屬性指的是子類中的this
,由於父類中是給this
綁定屬性的,所以子類也就繼承了父類的屬性和方法。構造函數繼承並沒有涉及原型prototype,所以父類的原型方法不會被子類繼承,子類的每個實例會單獨擁有一份父類的屬性方法而不能共用,如果想被子類繼承就必須放在構造函數中,要實現這樣的效果可以采用組合繼承的方式。
組合繼承
類式繼承是通過子類的原型prototype對父類實例化來實現的,構造函數繼承是通過在子類的構造函數作用環境中執行一次父類的構造函數來實現的,而組合繼承則同時做到這兩點。
// 聲明父類
function Parent(name){
this.name = name;
this.values = [‘A‘,‘B‘,‘C‘];
}
Parent.prototype.getName = function(){
console.log(this.name);
}
// 聲明子類
function Child(name,id){
Parent.call(this, name);
this.id = id;
}
Child.prototype = new Parent();
Child.prototype.getId = function(){
console.log(this.id);
}
var child1 = new Child(‘child1‘, 1);
child1.values.push(‘D‘);
console.log(child1.values); // ["A", "B", "C", "D"]
child1.getName(); // child1
child1.getId(); // 1
var child2 = new Child(‘child2‘, 2);
console.log(child2.values); // ["A", "B", "C"]
child2.getName(); // child2
child2.getId(); // 2
子類的實例中更改父類繼承下來的引用類型屬性,不會影響到其它實例,並且子類實例化過程中又能將參數傳遞到父類的構造函數中。
多態
多態就是同一個方法多種調用方式,JavaScript可以通過對傳入的參數列表arguments
進行判斷來實現多種調用方式。例如:
function Add(){
// 無參數
function zero(){
return 0;
}
// 一個參數
function one(num){
return num;
}
// 兩個參數
function two(num1, num2){
return num1 + num2;
}
this.add = function(){
// 獲取參數列表及參數個數
var arg = arguments,
len = arg.length;
switch(len){
case 0:
return zero();
case 1:
return one(arg[0]);
case 2:
return two(arg[0], arg[1]);
}
}
}
var A = new Add();
console.log(A.add()); // 0
console.log(A.add(1)); // 1
console.log(A.add(1,2)); // 3
當調用add進行運算時,會根據參數列表的不同做相應的運算,這就是JavaScript的多態實現方式。
總結
面向對象設計方法的應用解決了傳統結構化開發方法中客觀世界描述工具與軟件結構的不一致性問題,縮短了開發周期,解決了從分析和設計到軟件模塊結構之間多次轉換映射的繁雜過程,是一種高效率的軟件開發方式,特別是在多人協作開發的情況下,可以提高代碼的可復用性和維護性,使開發更有效率。
繼承有類式繼承,構造函數繼承人,組合繼承