- 工廠模式
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('a', 18, 'coder');
var person2 = createPerson('a', 18, 'coder');
工作模式是通過一個函數(shù)返回一個對象钢坦,但是有個問題是:新建對象實例和普通的調用函數(shù)沒有辨識度。
- 構造函數(shù)模式
function Person(){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
alert(this.name);
}
}
var person1 = new Person('a', 18, 'coder');
var person2 = new Person('b', 18, 'coder');
- 和工廠模式相比咕村,構造函數(shù)模式场钉,沒有顯示地創(chuàng)建對象,并返回懈涛,而是直接將屬性和“靜態(tài)“方法賦值給this對象逛万。使用new操作符新建實例,this對象引用的是以Person對象為模板的新對象批钠。
- 使用new操作符這種方式調用構造函數(shù)宇植,實際上是經歷了下面四個步驟:
- 創(chuàng)建一個新對象;
- 將構造函數(shù)的作用域賦值給新對象(因此this對象引用的是新對象埋心,而不是原型對象)指郁;
- 執(zhí)行構造函數(shù)的代碼;
- 返回這個新對象拷呆。
上面的工廠模式也是經歷的這幾個步驟闲坎,不過工廠模式是在構造函數(shù)中使用new操作符新建對象實例疫粥。
?
使用構造函數(shù)模式,解決了“標識函數(shù)和類型”的問題腰懂,但是如果在構造函數(shù)內部新建函數(shù)就會有個問題:
在實例person1和person2中都sayName函數(shù)梗逮,雖然功能相同,
但是他們卻是開辟了兩個空間绣溜,新建了兩個函數(shù)慷彤,因為他們各自在不同的作用域中。
避免重復新建同樣功能函數(shù)怖喻,避免冗余可以這樣做:
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = sayName;
}
function sayName(){
alert(this.name);
}
這樣做也帶來一個新的問題:
為了避免同功能函數(shù)的冗余底哗,然后我們把這些”靜態(tài)函數(shù)”都放在了全局作用域中,而在新對象中進行引用锚沸。但是一旦函數(shù)多起來跋选,封裝性就會失去,一堆函數(shù)在全局作用域咒吐,這樣也污染全局作用域野建。我自己的一個折中的做法是:用“+function(){ /*代碼/ }()”包裹属划。js提供另外一種方法:原型模式*
- 原型模式
構造函數(shù)會有一個prototype屬性恬叹,他存放的是一個指針,指向原型對象同眯,實例也是有個功能相同的內部屬性[[prototype]] / __proto__(__proto__屬性只在FireFox绽昼、chrome、Safari得到支持)指向原型對象须蜗,而原型對象中則有一個屬性硅确,constructor,他指向構造函數(shù)明肮,而prototype是原型模式的核心:
- prototype / [[prototype]]菱农,該屬性存放的是一個指針,指向原型對象柿估;
- 原型對象的屬性:constructor循未,該屬性存放的是一個指針,指向構造函數(shù)秫舌;
+function(){
function Person(){}
Person.prototype.name = 'issac';
Person.prototype.age = 18;
Person.prototype.sayName = function(){
console.log(this.name);
}
}();
var person1 = new Person('a', 18);
更簡單的原型語法
將對象賦值給prototype屬性的妖,即使用字面量的方法簡寫為:
Person.prototype = {
sayName: function(){ console.log(this.name) }
};
這樣雖然可以很好的簡寫,但要注意的一點是足陨,這樣做相當于重寫了原型對象嫂粟,constructor不再指向Person,而是指向Object構造函數(shù)墨缘。如果要使用到這個屬性星虹,就要手動將constructor指向Person
Person.prototype = {
constructor: Person,
sayName: function(){ console.log(this.name) }
};
這樣做零抬,也并非和默認獲得的constructor屬性相同,因為該屬性是手動添加的宽涌,那么內部屬性[[Enumerable]]是默認為true的媚值,而默認獲得的constructor的枚舉是false的』ぬ牵可以使用defineProperty方法設置
Object.defineProperty(Person.proptotype, 'constructor', {
enumerable: fase,
value: Person
});
- 只用原型模式的問題
原型模式最大的問題是由他的共享性引起褥芒,在屬性還是基本類型的時候還沒有什么問題,但如果是引用類型嫡良,問題就很明顯了锰扶。
function Person(){}
Person.prototype.name = 'issac';
Person.prototype.arr = ['issac', 'is', 'iron'];
var p1 = new Person();
var p2 = new Person();
p1.arr.push('man');
console.log(p1.arr); //['issac', 'is', 'iron', 'man']
console.log(p2.arr); //['issac', 'is', 'iron', 'man']
如果一開始就是打算共享這個數(shù)組還好說,但是更多時候這種情況是希望每個對象實例擁有獨立的數(shù)組寝受。這也是坷牛,比較少單獨使用原型模式的原因。
?
- 小插曲:實例對象中建立和原型對象屬性同名的屬性很澄,會屏蔽原型對象的屬性京闰,就算將實例對象中的同名屬性賦值為null也不能取消屏蔽,但是可以使用 delete 操作符 刪除實例對象中的同名屬性甩苛,可以重新訪問到原型對象中的同名屬性蹂楣。
?- hasOwnProperty可以用來判斷屬性是不是屬于實例對象
- 組合原型模式和構造函數(shù)模式
從上面的構造函數(shù)模式和原型模式,可以知道讯蒲,構造函數(shù)模式痊土,定義的屬性和方法是非共享的,在新建對象實例時復制了一份屬性副本(沒有必要在這里定義方法墨林,因為方法功能一樣赁酝,不必給每個實例分配一個副本,這是冗余旭等,浪費內存)酌呆,都是獨立的;而用原型模式構造的屬性和方法是共享的搔耕,每個屬性調用的原型屬性和方法都是共享的隙袁,相當于靜態(tài)的屬性/方法。
結合這兩個特點度迂,可以以構造函數(shù)模式定義實例屬性/方法(非共享)藤乙,以原型模式定義靜態(tài)方法/屬性(共享)
//構造實例屬性
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.not_share = ['issac', 'frank'];
}
//靜態(tài)的共享屬性/方法
Person.prototype = {
sayName: function(){
console.log(this.name);
},
share: ['issac', 'person'],
};
var p1 = new Person('a', 18, 'coder');
var p2 = new Person('b', 18, 'coder');
p1.share.push('dube');
p1.not_share.push('dube');
console.log(p1.share);
console.log(p2.share);
console.log(p1.not_share);
console.log(p2.not_share);
- 寄生構造函數(shù)模式
個人是認為這種模式適合用在原生對象上進行擴展,這樣也很適合“寄生”這一詞惭墓。
//給Array添加一個特殊的方法坛梁,但是有不污染Array對象
function SpecialArray(){
var values = new Array();
values.push.apply(values, arguments);
values.toPipedString = function(){
return this.join("|");
};
return values;
}
var arr = new SpecialArray('red', 'white', 'new');
console.log(arr.toPipedString());
一個小插曲,也是寫到這里才知道腊凶,函數(shù)可以不寫形參划咐,而直接從arguments對象(數(shù)組)中獲取實參拴念。
- 小結:主要介紹了以下五種創(chuàng)建對象的模式。
- 工廠模式;
- 構造函數(shù)模式褐缠;
- 原型模式政鼠;
- 組合原型模式和構造函數(shù)模式;
- 寄生構造函數(shù)模式队魏;