建立物件
使用new Object()
或者物件字面量都可以建立物件,但是這樣建立的物件過於簡單,不易於物件的屬性與方法的擴充套件與繼承。
下面講的物件可以與JavaEE
中的bean
做類比。
工廠模式
對,首先可能想到的是使用設計模式中的工廠模式
function createPizza(type) {
var o = new Object();
o.type = type;
o.bake = function() {
alert(`Start~`);
alert(this.type);
alert(`End~`);
};
return o;
}
var cheesePizza = createPizza(`cheese`);
var veggiePizza = createPizza(`veggie`);
cheesePizza.bake();
優點
工廠模式解決了建立多個類似物件的問題
缺點
物件無法識別,即建立出來的物件無法通過instanceof
等分析出屬於哪種型別
建構函式模式
用建構函式可用來建立特定型別的物件
// 建構函式首字母遵循OO語言慣例進行大寫
function Pizza(type) {
this.type = type;
this.bake = function() {
alert(`Start~`);
alert(this.type);
alert(`End~`);
};
}
var cheesePizza = new Pizza(`cheese`);
var veggiePizza = new Pizza(`veggie`);
cheesePizza.bake();
與工廠模式相比:
- 沒有在方法中顯示創造物件(o);
- 直接將屬性與方法賦值給this;
- 沒有
return
語句
在用new
的時候,會經歷一下4步:
- 建立一個新物件
- 將建構函式的作用域賦值給新物件(此時this指向新物件)
- 執行建構函式程式碼(為物件新增屬性)
- 返回新物件
如果不使用new,將建構函式當做函式使用,則this指向Global物件(在瀏覽器中為window物件),當然,可以使用call方法來指定作用域,例如
var o = new Object();
Pizza.call(o, `salty`);
o.bake();
使用建構函式方法,每個例項物件都有一個constructor
建構函式屬性,該屬性指向Pizza(使用物件字面量、工廠模式方法建立的物件該屬性指向Object)
cheesePizza.constructor == Pizza
檢查某個物件屬於哪種型別,一般使用instanceof
,cheesePizza同時屬於Pizza
與Object
(之所以屬於Object,是因為所有物件均繼承於Object)
cheesePizza instanceof Pizza;
cheesePizza instanceof Object;
優點
與工廠模式相比,建構函式模式能夠識別出物件型別
與下面的原型模式相比,能夠實現物件屬性的互相獨立,在引用型別屬性上很有用
缺點
每個例項物件的方法都是獨立的,導致方法不能夠共享
原型模式
每個函式(不是例項物件)都有一個prototype
屬性,該屬性是一個指標,指向一個物件,物件的用途是包含所有例項共享的屬性和方法。prototype通過呼叫建構函式建立的那個物件例項的原型物件。使用原型物件的好處是可以讓所有例項物件共享屬性與方法。
function Pizza() {
}
Pizza.prototype.type = `original`
Pizza.prototype.bake = function() {
alert(`Start~`);
alert(this.type);
alert(`End~`);
};
var cheesePizza = new Pizza();
cheesePizza.type = `cheese`;
var veggiePizza = new Pizza();
veggiePizza.type = `veggie`;
cheesePizza.bake();
veggiePizza.bake();
各個物件共享屬性與方法,同時每個物件都可以建立自己的屬性,並遮蔽掉原型物件的同名屬性,因為共享屬性與方法,所以以下等式成立
cheesePizza.bake == veggiePizza.bake
物件字面量重寫原型物件
也可以通過物件字面量來重寫整個原型物件:
Pizza.prototype = {
type: `original`,
bake: function() {
alert(`Start~`);
alert(this.type);
alert(`End~`);
}
}
這樣完全重寫,原型物件上的constructor
屬性不再指向Pizza
函式(全新的constructor指向Object),不過不影響通過instanceof
來識別物件型別。如果constructor
特別重要的話,可以顯式將它置為適當的值:
Pizza.prototype = {
constructor: Pizza,
type: `original`,
bake: function() {
alert(`Start~`);
alert(this.type);
alert(`End~`);
}
}
不過這種方式會將constructor
的屬性特徵變為可列舉,而預設情況下它是不可列舉的,如果想不可列舉,可以使用Object.defineProperty()
方法。
原型的動態性
對原型物件的修改會體現在例項物件上,即使例項物件先被建立。但是通過物件字面量重寫的原型物件則沒有該動態性
優點
定義在原型物件上的屬性,能夠保證在各例項物件上的共享
缺點
對於引用型別的屬性,各例項的共享會導致額外的問題。
組合使用建構函式模式與原型模式
整合建構函式模式與原型模式,建構函式模式用於定義例項屬性,原型模式用於定義方法和共享屬性。
動態原型模式
寄生建構函式模式
穩妥建構函式模式
各建立模式在Chrome瀏覽器中的表現
可以通過Chrome瀏覽器觀察使用工廠模式建立的cheesePizza物件屬性為:
cheesePizza
{type: "cheese", bake: ƒ}
bake: ƒ ()
type: "cheese"
__proto__:
constructor: ƒ Object()
hasOwnProperty: ƒ hasOwnProperty()
isPrototypeOf: ƒ isPrototypeOf()
propertyIsEnumerable: ƒ propertyIsEnumerable()
toLocaleString: ƒ toLocaleString()
toString: ƒ toString()
valueOf: ƒ valueOf()
__defineGetter__: ƒ __defineGetter__()
__defineSetter__: ƒ __defineSetter__()
__lookupGetter__: ƒ __lookupGetter__()
__lookupSetter__: ƒ __lookupSetter__()
get __proto__: ƒ __proto__()
set __proto__: ƒ __proto__()
使用建構函式模式建立cheesePizza物件屬性為:
cheesePizza
Pizza {type: "cheese", bake: ƒ}
bake: ƒ ()
type: "cheese"
__proto__:
constructor: ƒ Pizza(type)
__proto__:
constructor: ƒ Object()
hasOwnProperty: ƒ hasOwnProperty()
isPrototypeOf: ƒ isPrototypeOf()
propertyIsEnumerable: ƒ propertyIsEnumerable()
toLocaleString: ƒ toLocaleString()
toString: ƒ toString()
valueOf: ƒ valueOf()
__defineGetter__: ƒ __defineGetter__()
__defineSetter__: ƒ __defineSetter__()
__lookupGetter__: ƒ __lookupGetter__()
__lookupSetter__: ƒ __lookupSetter__()
get __proto__: ƒ __proto__()
set __proto__: ƒ __proto__()
使用原型模式建立cheesePizza物件屬性為:
cheesePizza
Pizza {type: "cheese"}
type: "cheese"
__proto__:
bake: ƒ ()
type: "original"
constructor: ƒ Pizza()
__proto__:
constructor: ƒ Object()
hasOwnProperty: ƒ hasOwnProperty()
isPrototypeOf: ƒ isPrototypeOf()
propertyIsEnumerable: ƒ propertyIsEnumerable()
toLocaleString: ƒ toLocaleString()
toString: ƒ toString()
valueOf: ƒ valueOf()
__defineGetter__: ƒ __defineGetter__()
__defineSetter__: ƒ __defineSetter__()
__lookupGetter__: ƒ __lookupGetter__()
__lookupSetter__: ƒ __lookupSetter__()
get __proto__: ƒ __proto__()
set __proto__: ƒ __proto__()