一晨逝、工廠模式
工廠模式解決了創(chuàng)建多個相似對象的問題犁享,但卻沒有解決對象識別的問題——即知道一個對象的類型
function createPerson(name, age, job) {
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function () {
alert(this.name);
};
return o;
}
var person1 = createPerson("Nicholas", 29, "Software Engineer");
var person2 = createPerson("Greg", 27, "Doctor");
二胎撤、構(gòu)造函數(shù)模式
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = function () {
alert(this.name);
};
}
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");
2.1 和工廠函數(shù)的區(qū)別:
- 沒有顯示的創(chuàng)建對象
- 直接將屬性和方法賦給了
this
對象 - 沒有
return
語句 - 函數(shù)名首字母大寫
創(chuàng)建Person
的新實(shí)例恤批,必須用new
操作符。以這種方式調(diào)用構(gòu)造函數(shù)實(shí)際上會經(jīng)歷以下4個步驟:
- 創(chuàng)建一個新對象
- 將構(gòu)造函數(shù)的作用域賦值給新對象(因此
this
就指向了這個新對象) - 執(zhí)行這個構(gòu)造函數(shù)中的代碼(為這個新對象添加屬性)
- 返回新對象
這里例子中person1
和person2
分別保存著Person
的一個不同的實(shí)例唱凯。這兩個對象都有一個constructor(構(gòu)造函數(shù))
屬性,該屬性指向Person
alert(person1.constructor == Person) // true
alert(person2.constructor == Person) // true
對象的constructor
屬性最初是用來標(biāo)識對象類型的谎痢。但是檢測對象類型磕昼,還是instanceof
更可靠。
創(chuàng)建自定義的構(gòu)造函數(shù)意味著將來可以將它的實(shí)例標(biāo)識為一種特定的類型节猿。這正是構(gòu)造函數(shù)勝過工廠模式的地方票从。
2.2 構(gòu)造函數(shù)和普通函數(shù)的區(qū)別
構(gòu)造函數(shù)和其他函數(shù)唯一的區(qū)別漫雕,就在于調(diào)用它們的方式不同。任何函數(shù)峰鄙,只要通過new
操作符調(diào)用浸间,那它就可以作為構(gòu)造函數(shù);而任何函數(shù)吟榴,如果不通過new
操作符來調(diào)用魁蒜,那它和普通函數(shù)也不會有什么兩樣。
// 當(dāng)做構(gòu)造函數(shù)使用
var person = new Person("Nicholas",29,"Software Engineer")
person.sayName(); //"Nicholas"
// 作為普通函數(shù)調(diào)用
Person("Greg",27,"Doctor") // 添加到window
window.sayName(); //"Greg"
// 在另一個對象的作用域中調(diào)用
var o = new Object();
Person.call(o,"Kristen",25,"Nurse")
o.sayName(); //"Kristen"
2.3 構(gòu)造函數(shù)的問題
每個方法都要在每個實(shí)例上重新創(chuàng)建一遍吩翻《悼矗可以通過在全局作用域中創(chuàng)建函數(shù)解決這一問題,但這樣讓引用類型沒有封裝性狭瞎。
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = sayName;
}
function sayName() {
alert(this.name);
}
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");
console.log(person1, person2);
三铣减、原型模式
我們創(chuàng)建的每個函數(shù)都有一個`prototype`(原型)屬性,這個屬性是一個指針脚作,這個對象的用途是包含可以由特定類型的所有實(shí)例共享的屬性和方法葫哗。使用原型的好處是可以讓所有對象實(shí)例共享它包含的屬性和方法。
function Person() {}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function () {
alert(this.name);
};
var person1 = new Person();
person1.sayName();
var person2 = new Person();
person2.sayName();
alert(person1.sayName == person2.sayName);
3.1 理解原型對象
無論什么時候球涛,只要創(chuàng)建了一個新函數(shù)劣针,就會根據(jù)一組特定的規(guī)則為該函數(shù)創(chuàng)建一個prototype
屬性,這個屬性指向函數(shù)的原型對象亿扁。在默認(rèn)情況下捺典,所有原型對象都會自動獲得一個constructor(構(gòu)造函數(shù))
屬性,這個屬性包含一個指向prototype
屬性所在函數(shù)的指針从祝。
- 原型對象的缺點(diǎn)
原型中所有屬性是被很多實(shí)例共享襟己,這種共享對于函數(shù)非常合適。對于包含基本值的屬性也可以牍陌,對于包含引用類型的屬性來說擎浴,可能會被修改。
function Person() {}
Person.prototype = {
constructor: Person,
name: "Nicholas",
age: 29,
job: "Software Engineer",
friends: ["Shelby", "Court"],
sayName: function () {
alert(this.name);
},
};
var person1 = new Person();
var person2 = new Person();
person1.friends.push("Van");
alert(person1.friends); // "Shelby,Court,Van"
alert(person2.friends); // "Shelby,Court,Van"
alert(person1.friends === person2.friends); // true
四毒涧、組合使用構(gòu)造函數(shù)模式和原型模式
構(gòu)造函數(shù)模式用于定義實(shí)例屬性贮预,而原型模式用于定義方法和共享的屬性。結(jié)果每個實(shí)例都會有自己的一份實(shí)例屬性的副本契讲,但同時又共享者對方付的引用仿吞,最大限度地節(jié)省了內(nèi)存。
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.friends = ["Shelby", "Court"];
}
Person.prototype = {
constructor: Person,
sayName: function () {
alert(this.name);
},
};
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");
person1.friends.push("Van");
alert(person1.friends); //"Shelby,Court,Van"
alert(person2.friends); // "Shelby,Court"
alert(person1.friends === person2.friends); // false
alert(person1.sayName === person2.sayName); // true
五捡偏、動態(tài)原型模式
可以將所有信息封裝在構(gòu)造函數(shù)中唤冈。
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
if (typeof this.sayName !== "function") {
Person.prototype.sayName = function () {
alert(this.name);
};
}
}
var friend = new Person("Nicholas", 29, "Software Engineer");
friend.sayName(); //"Nicholas"
使用動態(tài)模式時,不能使用對象字面量重寫原型银伟。如果在已經(jīng)創(chuàng)建了實(shí)例的情況下重寫原型你虹,那么就會切斷現(xiàn)有實(shí)例與新實(shí)例之間的聯(lián)系
六凉当、寄生構(gòu)造函數(shù)模式
通常,在前述幾種模式都不適用的情況下售葡,可以使用寄生構(gòu)造函數(shù)模式看杭。這種模式的基本思想是創(chuàng)建一個函數(shù),該函數(shù)的作用僅僅是封裝創(chuàng)建對象的代碼挟伙,然后再返回新創(chuàng)建的對象楼雹;
function Person(name, age, job) {
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function () {
alert(this.name);
};
return o;
}
var friend = new Person("Nicholas", 29, "Software Engineer");
friend.sayName(); //"Nicholas"
例如:
function SpecialArray() {
// 創(chuàng)建數(shù)組
var values = new Array();
//添加值
values.push.apply(values, arguments);
//添加方法
values.toPipedString = function () {
return this.join("|");
};
//返回數(shù)組
return values;
}
var colors = new SpecialArray("red", "green", "blue");
alert(colors.toPipedString());
返回的對象與在構(gòu)造函數(shù)外部創(chuàng)建的對象沒什么不同
七、穩(wěn)妥構(gòu)造函數(shù)模式
所謂穩(wěn)妥對象尖阔,指的是沒有公共屬性贮缅,而且其方法也不引用this
的對象。
與寄生構(gòu)造函數(shù)的區(qū)別:
- 創(chuàng)建對象的實(shí)例方法不引用
this
- 不使用
new
操作符調(diào)用構(gòu)造函數(shù)
function Person(name, age, job) {
// 創(chuàng)建要返回的對象
var o = new Object();
//添加方法
o.sayName = function () {
alert(name);
};
// 返回對象
return o;
}
var friend = Person("Nicholas", 29, "Software Engineer");
friend.sayName(); //"Nicholas"