使用構造函數 基本思想: 在子類型構造函數的內部調用超類型構造函數。 使用apply()和call()方法可以在新創建的對象上執行構造函數。 function SuperType() { this.colors = ["red", "blue", "green"]; } function SubTy ...
使用構造函數
基本思想:在子類型構造函數的內部調用超類型構造函數。
使用apply()和call()方法可以在新創建的對象上執行構造函數。
function SuperType() {
this.colors = ["red", "blue", "green"];
}
function SubType() {
SuperType.call(this); // 調用超類構造函數
}
var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors); // "red", "blue", "green", "black"
var instance2 = new SubType();
alert(instance2.colors); // "red", "blue", "green"
通過call()或apply()方法,實際是在將來要創建的SubType實例的環境下調用SupType構造函數。這樣,SubType的實例都會具有自己的colors屬性副本
傳遞參數
使用構造函數的一個好處是可以在子類構造函數中向超類構造函數傳遞參數。
function SuperType(name) {
this.name = name;
}
function SubType() {
SuperType.call(this, "蕭蕭弈寒");
this.age = 18;
}
var instance = new SubType();
alert(instance.name);
alert(instance.age);
上例中,在SubType構造函數內部調用了SuperType構造函數時,實際上是為SubType的實例設置了name屬性。為了確保SuperType構造函數不會重寫子類的屬性,可以在調用超類型構造函數之後,再添加屬性。
使用構造函數的問題
使用構造函數,方法都在構造函數中定義,函數就不能復用了。而且,在超類的原型中定義的方法,對子類型是不可見的。這導致了所有類型都只能使用構造函數模式。
組合繼承
組合繼承:指的是將原型鏈和使用構造函數組合使用,取二者之長的一種繼承模式。思想:使用原型鏈實現對原型屬性和方法的繼承,通過使用構造函數來實現對實際屬性的繼承。
function SuperType(name) {
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function () {
alert(this.name);
};
function SubType(name, age) {
// 繼承超類屬性
SuperType.call(this, name);
// 自定義屬性
this.age = age;
}
// 繼承方法
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function () {
alert(this.age);
};
var instance1 = new SubType("蕭蕭弈寒", 18);
instance1.colors.push("black");
alert(instance1.colors);
instance1.sayName();
instance1.sayAge();
var instance2 = new SubType("xxyh", 19);
alert(instance2.colors);
instance2.sayName();
instance2.sayAge();
上例中,SuperType構造函數定義了兩個屬性:name和colors。SuperType原型定義了sayName()方法。SubType構造函數在調用SuperType構造函數時傳入了name參數,然後定義了自己的屬性age。然後將SuperType的實例賦值給SubType的原型,然後又在給新原型上定義了方法sayAge()。這樣,就可以使兩個不同的SubType實例既有自己的屬性,又使用相同的方法。
原型式繼承
基本思想:藉助原型鏈可以基於已有的對象創建新對象,同時不必因此創建自定義類型。
示例:
function object(obj) {
function F() {
}
F.prototype = obj;
return new F();
}
在object()函數內部,先創建了一個臨時性的構造函數。然後將傳入的對象作為這個構造函數的原型,最後返回這個臨時類型的新實例。
object對傳入的對象執行了一次淺拷貝:
var person = {
name: "蕭蕭弈寒",
friends: ["張三", "李四", "王五"]
};
var anotherPerson1 = object(person);
anotherPerson1.name = "葫蘆娃";
anotherPerson1.friends.push("趙六");
var anotherPerson2 = object(person);
anotherPerson2.name = "魁拔";
anotherPerson2.friends.push("錢七");
alert(person.friends); // "張三", "李四", "王五","趙六","錢七"
這種方式要求一個對象可以作為另一個對象的基礎。如果有這種對象,把它傳遞給object,然後對得到的對象加以修改。
在上面的例子中,person作為另一個對象的基礎,將person傳入object()函數中,然後改函數就會返回一個新對象,這個對象將person作為原型,所以它的原型中就包含一個基本類型值屬性和一個引用類型值屬性。這樣,person.friends不僅屬於person,而是會被anotherPerson1、anotherPerson2共用。
ECMAScript5通過新增Object.create()規範了原型式繼承。這個方法接收兩個參數:一個用作新對象原型的對象和一個為新對象定義額外屬性的對象。
var person = {
name: "蕭蕭弈寒",
friends: ["張三", "李四", "王五"]
};
var anotherPerson1 = Object.create(person);
anotherPerson1.name = "魁拔";
anotherPerson1.friends.push("趙六");
var anotherPerson2 = Object.create(person);
anotherPerson2.name = "猴子";
anotherPerson2.friends.push("錢七");
alert(person.friends); // "張三", "李四", "王五","趙六","錢七"
以這種方式指定的任何屬性都會覆蓋原型對象上的同名屬性。
var person = {
name: "蕭蕭弈寒",
friends: ["張三", "李四", "王五"]
};
var anotherPerson = Object.create(person, {
name: {
value: "魁拔"
}
});
alert(anotherPerson.name); // 魁拔
寄生式繼承
思路:創建一個僅用於封裝繼承過程的函數,該函數在內部以某種方式來增強對象,最後再像真的是它做了所有工作一樣返回對象。
function createAnother(original) {
var clone = object(original);
clone.sayHi = function () {
alert("hi");
};
return clone;
}
上面,createAnother()接收了一個參數,這個參數就是作為新對象基礎的對象。然後把這個對象傳遞給object()函數,將返回的結果賦值給clone。再為clone對象添加一個新方法sayHi(),最後返回clone對象。
var person = {
name: "蕭蕭弈寒",
friends :["張三", "李四", "王五"]
};
var anotherPerson = createAnother(person);
anotherPerson.sayHi(); // "hi"
新對象不僅具有person的所有屬性和方法,而且還有自己的sayHi()方法。
寄生組合式繼承
寄生組合式繼承:通過構造函數來繼承屬性,通過原型鏈的混成形式來繼承方法。不必指定子類型的原型而調用超類型的構造函數,只需要超類型原型的一個副本。
寄生組合式繼承基本模式:
function inheritPrototype(subType, superType) {
var prototype = object(superType.prototype);
prototype.constructor = subType; // 增強對象
subType.prototype = prototype; // 指定對象
}
上面的例子中,這個函數接收兩個參數:子類型的構造函數和超類型的構造函數。第一步是創建超類型原型的一個副本。第二步是為創建的副本添加constructor屬性,從而彌補因重寫原型而失去的預設的constructor屬性。最後將新對象賦值給子類型的原型。
示例:
function SuperType(name) {
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function () {
alert(this.name);
};
function SubType(name, age) {
SuperType.call(this, name);
this.age = age;
}
inheritPrototype(SubType, SuperType);
SubType.prototype.sayAge = function () {
alert(this.age);
};
圖解:
這個例子中,只調用了一次SuperType構造函數。避免了在SubType原型上創建不必要的屬性。而且,能夠保持原型不變。