創建對象
- 工廠模式
function createPerson(name, age){
var o = new Object();
o.name = name;
o.age = age;
return o;
}
// 使用
var p1 = createPerson('zph', 18);
這樣做就找不到原型關系了,沒有解決對象識別問題。
- 構造函數
function Person(name, age){
this.name = name;
this.age = age;
this.showName = function(){ return this.name; };
}
// 使用
var p1 = new Person('zph', 18);
var p2 = new Person('zhh', 14);
解決了對象識別問題,但多個對象實例不能共享相同的方法,冗余。
- 原型模式
function Person(){
}
Person.prototype.name = '';
Person.prototype.age= 0;
// 使用
var p1 = new Person();
var p2 = new Person();
所有對象實例共享屬性和方法,可能導致數據出錯。
- 組合模式(組合使用構造函數和原型模式)
function Person(){
this.name = '';
this.age = 0;
}
Person.prototype.showName = function() { return this.name; };
// 使用
var p1 = new Person();
var p2 = new Person();
目前最流行的方式。
- 動態原型模式
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
if (typeof this.showName != "function"){
Person.prototype.showName = function(){ return this.name; };
}
}
僅第一次初始原型。
- 寄生構造函數模式
function Person(name, age){
var o = new Object();
o.name = name;
o.age = age;
return o;
}
// 使用
var p1 = new Person('zph', 18);
與工廠函數唯一的不同,是使用了new
操作符,這會導致創建兩次對象。
- 穩妥構造函數模式
function createPerson(name, age){
var o = new Object();
var _name = name;
var _age = age;
o.showName = function() { return _name; };
return o;
}
// 使用
var p1 = createPerson('zph', 18);
只可以通過指定方法獲取相應的值,安全。
繼承
- 原型鏈繼承
function Animal() {
this.name = 'Animal';
}
Animal.prototype.showName = function () { return this.name; };
// 繼承
function Cat () {
this.name = 'Cat';
}
Cat.prototype = new Animal();
引用類型會被共享。
- 借用構造函數繼承
function SuperType(){
this.colors = ["red", "blue", "green"];
}
function SubType(){
//繼承了SuperType
SuperType.call(this);
}
這就又沒辦法復用函數了,另外也失去了繼承關系,由于沒有原型鏈 ,無法識別 SubType
是 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;
為了繼承屬性和共享方法,兩次調用了 SuperType,同時對象自身和原型鏈都會保存一份屬性,造成數據冗余。
- 原型式繼承
function object(o){
function F(){}
F.prototype = o;
return new F();
}
// 使用
var p1 = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
};
// 基于已有的 p1 創建新對象 p2
var p2 = object(p1);
基于已有的對象創建新對象,ES5 將這種原型式繼承規范化為 Object.create()
- 寄生式繼承
function createAnother(original){
var clone = Object(original); // 通過調用函數創建一個新對象
clone.sayHi = function(){ // 增強這個對象
alert("hi");
};
return clone; // 返回這個對象
}
var person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
};
// 使用
var anotherPerson = createAnother(person);
也是基于已有的對象創建新對象。(這里的 Object()
函數調用有疑問,見下)
- 寄生組合式繼承(通過借用構造函數來繼承屬性,通過原型鏈的混成形式來繼承方法)
function inheritPrototype(subType, superType){
var prototype = Object(superType.prototype); //創建對象
prototype.constructor = subType; //增強對象
subType.prototype = prototype; //指定對象
}
// 使用
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);
};
以上是高程上的摘抄(有少量改動),其中寄生組合式繼承略有疑問,不知道是不是高程寫錯了,這樣的方式破壞了原型鏈,因為:
Object(superType.prototype) === superType.prototype // true
Object(someObject)
是類型轉換,而不是創建對象,我想這里是一個翻譯錯誤,這里的 Object()
應該是指上文的 object()
函數,對應 Obect.create()
,已在圖靈社區提交勘誤,待續。
最后,記錄下自己的繼承實現方式:
function object(o){ // ES5 中 Object.create() 的 polyfill 函數,兩者效果一致
function F(){}
F.prototype = o;
return new F();
}
function inheritPrototype(subType, superType){
subType.prototype = object(superType.prototype); // 指定對象原型,或使用 ES5 的 Object.create()
subType.prototype.constructor = subType; // 修正構造函數
}
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);
};