面向對象就是把屬性和操作屬性的方法放在一起作為一個相互依存的整體——對象,即擁有類的概念,基於類可以創建任意多個實例對象,一般具有封裝、繼承、多態的特性!
ECMA-262把對象定義為:“無序屬性的集合,其屬性可以包含基本值 對象 或者函數”。這就是說對象是一組沒有特定順序的值,其中值可以是數據或者函數。
雖然Object構造函數或對象字面量都可以創建單個對象,但這些方式有個明顯的缺點,那就是使用同一個接口創造很多對象,會產生大量的重復代碼。所以產生了下面幾種模式。
1 工廠模式
function createPerson(name,age,job){
var o = {};
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
alert(this.name);
};
return o;
}
var tanya = createPerson("tanya","30","female");
tanya.sayName();
特點:這種模式抽象了創建具體對象的過程。它用函數來封裝以特定接口創建對象的細節。
弊端:沒有解決對象識別的問題,而且每次生成一個新對象,都要創建新函數sayName,這使得每個對象都有自己的sayName版本,而事實上,所有的對象都共享同一個函數.
2 構造函數模式
function createPerson(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
alert(this.name);
};
}
var tanya =new createPerson("tanya","30","female");
tanya.sayName();
PS:構造函數和普通函數的唯一區別就是調用他們的方式不同:任何函數,只要通過new操作符調用就是構造函數,反之就是普通函數。
使用new操作符創建新實例會經歷下面4個步驟:
(1) 創建一個新對象
(2) 將構造函數的作用域賦給新對象(就是this指向了這個新對象)
(3) 執行構造函數中的代碼(為這個新對象添加屬性)
(4) 返回新對象
構造函數和工廠模式的不同之處:
- 沒有顯示的創建對象
- 直接將屬性和方法賦給this對象;
- 沒有return語句
構造函數解決了對象識別的問題(tanya有一個constructor屬性,該屬性指向createPerson),但是就像工廠模式一樣,每個方法都會在每個實例中重新創建一遍。我們可以把函數定義轉移到構造函數外部來解決這個問題,但是這樣就沒有封裝性可言了。
3 原型模式
首先介紹一下prototype(原型)屬性,我們創建的每個函數都有一個prototype屬性,這個屬性是一個指針,指向一個對象,而這個對象的用途是包含可以由特定類型的所有實例共享的屬性和方法。
PS:JS中萬物皆對象,但分為兩大類:普通對象和函數對象。所有的函數對象都有一個prototype屬性,普通對象是沒有prototype屬性的,只有_proto_。
function createPerson(){
}
createPerson.prototype.name = "tanya";
createPerson.prototype.age = "30";
createPerson.prototype.job = "female";
createPerson.prototype.sayName = function(){
alert(this.name);
};
var tanya =new createPerson();
tanya.sayName();
原型模式雖然解決了構造函數每個方法都會在每個實例中重新創建一遍的問題。但是所有實例在默認情況下都取得了相同的屬性值,實例一般都是要有屬於自己的全部屬性的。
4 組合使用構造函數模式和原型模式
function createPerson(name,age,job){
this.name = name;
this.age = age;
this.job = job;
}
createPerson.prototype.sayName = function(){
alert(this.name);
};
var tanya =new createPerson("tanya","30","female");
tanya.sayName();
這是創建自定義類型的最常見方式,構造函數模式定義實例屬性,原型模式定義方法和共享的屬性。
5 寄生構造函數模式
function createPerson(name,age,job){
var o = {};
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
alert(this.name);
};
return o;
}
var tanya =new createPerson("tanya","30","female");
tanya.sayName();
除了使用new操作符來定義新的對象,以及將其稱之為構造函數之外,其他和工廠模式定義一模一樣。因為創建時用了new,因此使得實現的過程不一樣(但是實現過程不重要),結果一樣。看起來更優雅
寄生構造函數可以在構造函數不適應的情況使用,比如創建具有額外方法的已有類型(如數組,Date類型等),但是又不污染原有的類型。
6 穩妥構造函數模式
穩妥對象,指的是沒有公共屬性,其方法也不引用this的對象。
穩妥構造函數與寄生構造函數相似,但是有2點不同:新創建對象的實例方法不引用this,不使用new操作符調用構造函數。
function createPerson(name, age, job) {
var o = new Object();
// private members
var nameUC = name.toUpperCase();
// public members
o.sayName = function() {
alert(name);
};
o.sayNameUC = function() {
alert(nameUC);
};
return o;
}
var person = Person("Nicholas", 32, "software Engineer");
person.sayName(); // "Nicholas"
person.sayNameUC(); // "NICHOLAS"
alert(person.name); // undefined
alert(person.nameUC); // undefined
凡是想設為 private 的成員都不要掛到 createPerson 返回的對象 o 的屬性上面,掛上了就是 public 的了。這里的 private 和 public 都是從形式上類比其他 OO 語言來說的,其實現原理還是 js 中作用域、閉包和對象那一套。