面向對象的程序設計
6.1 理解對象
Object.defineProperties與Object.defineProperty
1. 數據屬性
- configurable
- enumerable
- writable
- value
2. 訪問器屬性
- get
- set
- configurable
- enumerable
"use strict";
var book = {
_year: 2004,//以下劃線開頭,用於表示只能通過對象方法訪問的屬性。_
edition: 1,
visTimes:0
};
Object.defineProperty(book, 'year', { //設置一個year訪問器屬性
get: function() {
this.visTimes++;
return this._year;
},
set: function(newValue) {
if (newValue > 2004) {
this._year = newValue;
this.edition += newValue - 2004;
}
},
enumerable:false,
configurable:true
});
book.year = 2005;
Object.getOwnPropertyDescriptor(book,"year");
通過設置自定義訪問器屬性的get和set方法,可以做許多事情,比如說記錄訪問次數。
- Object.getOwnPropertyDescriptor(book,”year”) 讀取屬性的特性
6.2 創建對象
6.2.1 工廠模式
抽象了創建具體對象的過程。
function createPerson (name,age,job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
console.log(this.name);
}
return o;
}
6.2.2 構造函數模式
構造與工廠的區別:
1. 沒有顯示的創建對象
2. 直接將屬性和方法賦給了this對象
3. 沒有return語句
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function () {
console.log(this.name);
}
}
var person = new Person("wilson",20,"student");
person.sayName(); //當構造函數使用
Person("wilson2",21,"student"); //當普通函數使用
window.sayName();
var o = new Object();
Person.apply(o,["wilson3",22,"student"]) //在另一個對象的作用域中調用
o.sayName();
構造函數的4個步驟:
1. 創建一個新對象
2. 將構造函數的作用域賦給新對象
3. 執行構造函數中的代碼
4. 返回新對象
注:創建自定義函數的構造函數意味着將他的實例標識爲一種特定的類型,這也是構造模式勝過工廠模式的地方。
構造函數的問題:每個方法都要在每個實例上重新創建一遍。
6.2.3 原型模式
我們創建的每一個函數都有一個prototype
屬性,這個屬性是一個指針,指向一個對象,而這個對象的用途是包含可以由特定類型的所有實例共享的屬性和方法。
原型的問題:
第一:如果原型中有屬性是引用類型的話,那麼所有繼承該原型的實例將共享着一個屬性的引用類型。
第二:創建子類型的實例時,無法向超類型的構造函數中傳遞參數。
6.2.4 組合使用構造函數模式和原型模式
構造函數模式用於定義實例屬性,原型模式用於定義方法和共享的屬性。結果,每個實例都會有自己的一份實例屬性的副本,
但同時共享着對方法的引用,最大限度的節省了內存。另外,還支持向構造函數傳遞參數。
6.2.5 動態原型模式
通過檢測某個應該存在的方法是否有效來決定是否需要初始化原型。
6.3 繼承
6.3.1 原型鏈
方法:將原型對象指向另一個類型的實例
6.3.2 借用構造函數
function SuperType(){
this.property = true;
}
function SubType(){
SuperType.call(this,"wilson"); //繼承了SuperType,同時還傳遞了參數
this.age = 20;
}
var instance = new SubType();
6.3.3 組合繼承
使用原型鏈實現對原型屬性和方法的繼承,而通過借用構造函數來實現對實例屬性的繼承。
組合繼承避免了原型鏈和借用構造函數的缺陷,融合了兩者的優點。
6.3.4 原型式繼承
基於原型可以基於已有對象來創建。
function object(o){
function F(){}
F.prototype = o;
return new F();
}
等價於ECMA5新增的Object.create()
6.3.5 寄生式繼承
與原型式繼承緊密相關,用於封裝繼承過程。
function createAnother(original){
"use strict";
var clone = Object.create(original);
clone.sayHi = function () {
alert("hi");
};
return clone;
}
6.3.6 寄生組合式繼承
因爲組合繼承在任何情況下都會調用兩次構造函數:一次是在創建子類型原型的時候,第二次是在子類型構造函數內部。
第一次創建子類型原型時,即會繼承構造函數的屬性,不過是存儲在原型對象上,第二次時在對象創建並覆蓋掉存儲在原型對象上的屬性。