創(chuàng)建對(duì)象
JS有六種數(shù)據(jù)數(shù)據(jù)類型,其中五種屬于基本數(shù)據(jù)類型:Null、Boolean、undefined、String、Number。
而其它值都是對(duì)象。數(shù)組是對(duì)象,函數(shù)是對(duì)象,正則表達(dá)式是對(duì)象。對(duì)象也是對(duì)象。
來看一下對(duì)象的定義:
無序?qū)傩缘募希鋵傩钥梢园局?、?duì)象、或者函數(shù)。
我們通過對(duì)象字面量的方式 創(chuàng)建對(duì)象。創(chuàng)建的對(duì)象用于我們想要做的事。但是,如果只有對(duì)象,那么可以想象我們寫的代碼將全是光禿禿的對(duì)象,會(huì)產(chǎn)生大量的重復(fù)代碼,和命名沖突等等問題。這是非常非常糟糕的。
為了解決這個(gè)問題,人們開始使用 工廠模式的一種變體。
工廠模式
工廠模式抽象了具體對(duì)象的過程。也就是說,發(fā)明了一種函數(shù),把對(duì)象放到函數(shù)里,用函數(shù)封裝創(chuàng)建對(duì)象的細(xì)節(jié)。
function createPerson (name,age) {
var o = {
name : name,
age : age,
sayName : function () {
alert(this.name)
}
}
return o;
}
var person1 = createPerson("Tom",14);
var person2 = createPerson("Jerry",18)
console.log(person1 instanceof Object) //true
console.log(person1 instanceof createPerson) //false
instanceof 用于檢測(cè)數(shù)據(jù)類型
var aa = []
console.log(aa instanceof Array) //true
工廠模式解決了代碼復(fù)用的問題,但是卻沒有解決對(duì)象識(shí)別的問題。即創(chuàng)建的所有實(shí)例都是Object類型。
為了解決這一問題,就有了構(gòu)造函數(shù)模式
構(gòu)造函數(shù)模式
function Person (name,age) {
this.name = name;
this.age = age;
this.sayName = function () {
alert(this.name)
}
}
var person1 = new Person('Tom',14);
var Person2 = new Person('Jerry',18);
- 構(gòu)造函數(shù) Person 有一個(gè)prototype(原型)屬性,這個(gè)屬性是一個(gè)指針,指向一個(gè)對(duì)象即:Person.prototype(原型對(duì)象);
- 實(shí)例person1 person2也有一個(gè)[[prototype]]屬性或者叫proto,這個(gè)屬性 也指向Person.prototype;
- 構(gòu)造函數(shù)、和實(shí)例 都共享Person.prototype里的 屬性和方法;
- Person.prototype里有一個(gè) constructor屬性,這個(gè)屬性也是一個(gè)指針,指向構(gòu)造函數(shù)Person。這樣以來,實(shí)例 也指向了Person,那么實(shí)例 也共享了構(gòu)造函數(shù)的屬性和方法。
- 構(gòu)造函數(shù)、實(shí)例、原型對(duì)象里所有的屬性和方法 都是共享的。
構(gòu)造函數(shù)解決了對(duì)象識(shí)別問題,我們?cè)谶@個(gè)例子中創(chuàng)建的對(duì)所有對(duì)象既是Object的實(shí)例,同時(shí),也是Person的實(shí)例。這一點(diǎn)通過instanceof操作符可以得到驗(yàn)證。
console.log(person1 instanceof Object) //true
console.log(person1 instanceof Person) //true
創(chuàng)建自定義的構(gòu)造函數(shù)意味著,將來可以將它的實(shí)例 標(biāo)識(shí)為一種特定類型;這正是構(gòu)造函數(shù)勝過工廠模式的地方。Array就是這種方式(我認(rèn)為的)。用構(gòu)造函數(shù)的方式,實(shí)例化一個(gè)新對(duì)象,這個(gè)對(duì)象可以是其它類型例如Array類型。
function Array () {
}
var arr = new Array()
arr instanceof Array // true
構(gòu)造函數(shù)與普通函數(shù)的區(qū)別
構(gòu)造函數(shù)和普通函數(shù)的唯一區(qū)別,在于調(diào)用它們的方式不同。
當(dāng)作構(gòu)造函數(shù)使用
function Person (name,age) {
console.log(this)
}
var person = new Person()
需要注意的是,this 指向 構(gòu)造函數(shù)Person
當(dāng)作普通函數(shù)使用
function Person (name,age) {
console.log(this)
}
Person()
this指向widow.
構(gòu)造函數(shù)的問題
構(gòu)造函數(shù)雖然好用,但也有缺點(diǎn)。既每個(gè)new出來的實(shí)例 里的方法都要重新創(chuàng)建一遍。在前面的例子中person1 person2 都有一個(gè)sayName方法,但這兩個(gè)方法不是同一個(gè)Function實(shí)例!每個(gè)實(shí)例的方法 都是不同的,不相等的。這是不合理的!
function Person (name) {
this.name = name;
this.sayName = new Function ("alert(this.name)")
}
function Person (name) {
this.name = name;
this.sayName = function () {
alert(this.name)
}
}
alert( person1.sayName == person2.sayName ) //false
由此可見,完成同樣任務(wù)的函數(shù)確實(shí)沒必要 每個(gè)實(shí)例,就實(shí)例一次。
于是,有需求,就有解決方案。原型模式。
原型模式
我們創(chuàng)建的每個(gè)函數(shù)都有一個(gè)prototype(原型)屬性,這個(gè)屬性是一個(gè)指針,指向一個(gè)對(duì)象,而這個(gè)對(duì)象的用途是包含可以由特定類型的所有實(shí)例共享的屬性和方法。
使用原型對(duì)象的好處是可以讓所有對(duì)象實(shí)例共享它所包含的屬性和方法。也就是說,不必在構(gòu)造函數(shù)中定義對(duì)象實(shí)例的信息,而是將這些信息添加到原型對(duì)象中。
廢話少說,那么到底 原型模式是如何解決 每個(gè)實(shí)例的方法 是同一個(gè)呢?
看代碼:
function Person (){
}
Person.prototype.name = "Tom";
Person.prototype.sayName = function () {
alert(this.name)
};
或者
Person.prototype = {
constructor : Person,
name : "Tom",
sayName : function () {
alert(this.name)
}
}
var person1 = new Person();
person1.sayName(); //"Tom"
var person2 = new Person();
person2.sayName(); //"Tom"
alert( person1.sayName == persona2.sayName ) //true
再來看下這個(gè):
- 構(gòu)造函數(shù) Person 有一個(gè)prototype(原型)屬性,這個(gè)屬性是一個(gè)指針,指向一個(gè)對(duì)象即:Person.prototype(原型對(duì)象);
- 實(shí)例person1 person2也有一個(gè)[[prototype]]屬性或者叫proto,這個(gè)屬性 也指向Person.prototype;
- 構(gòu)造函數(shù)、和實(shí)例 都共享Person.prototype里的 屬性和方法;
- Person.prototype里有一個(gè) constructor屬性,這個(gè)屬性也是一個(gè)指針,指向構(gòu)造函數(shù)Person。這樣以來,實(shí)例 也指向了Person,那么實(shí)例 也共享了構(gòu)造函數(shù)的屬性和方法。
- 構(gòu)造函數(shù)、實(shí)例、原型對(duì)象里所有的屬性和方法 都是共享的。
與構(gòu)造函數(shù)相比,
原型模式,把公共方法提出來放到prototype對(duì)象里。
每個(gè)實(shí)例 的[[prototype]]指針 指向這個(gè)對(duì)象,所以所有實(shí)例的公共方法 是同一個(gè)。這樣也避免了內(nèi)存浪費(fèi)。
原型模式的其它特性
略
詳情參見 《JS高程3》 第六章
原型模式的問題###
我們寫代碼的時(shí)候,很少只用到原型模式,說明它還是有坑的。
原型模式很大的優(yōu)點(diǎn) 是所有實(shí)例都共享屬性和方法。這個(gè)很好的優(yōu)點(diǎn) 對(duì)于函數(shù)來說非常合適,可對(duì)于屬性來說,有點(diǎn)不適應(yīng)這種開放的"熱情"。
看代碼:
function Person () {
}
Person.prototype = {
constructor : Person,
name : "Tom",
friends : ["Jerry","Sara"]
}
var person1 = new Person();
var person2 = new Person();
person1.friends.push("Vans");
alert( person1.friends ); //"Jerry","Sara","Vans"
alert( person2.friends ); //"Jerry","Sara","Vans"
alert( person1.friends === person2.friends ); //true
以上已經(jīng)很明了了,給一個(gè)實(shí)例添加屬性值,結(jié)果,所有實(shí)例的屬性值也改變了。實(shí)例應(yīng)該具有自己的獨(dú)立性。自己莫名其妙的被改變,肯定是有問題的。
有問題,就會(huì)有解決方案,再多坑,也抵不過我們前輩的不懈努力。
于是就有了 構(gòu)造函數(shù)和原型模式混合模式
組合使用構(gòu)造函數(shù)模式和原型模式
創(chuàng)建自定義類型最常見的方式,就是組合模式。
構(gòu)造函數(shù)模式用于定義實(shí)例屬性,而原型模式用于定義方法和共享的屬性。
結(jié)果,每個(gè)實(shí)例都有自己的一份實(shí)例屬性的副本。注意是是 副本。同時(shí),又共享著對(duì)方法的引用,最大限度地節(jié)省了內(nèi)存。
另外,這種混成模式還支持向構(gòu)造函數(shù)傳遞參數(shù)。
function Person (name,age) {
this.name = name;
this.age = age;
this.friends = ["Tom","Jerry"]
}
Person.prototype = {
consructor : Person,
sayName : function () {
alert(this.name)
}
}
var person1 = new Person("Abert",18);
var person2 = new Person("Marry",17);
person1.friends.push("Vans");
alert( person1.friends ); //"Tom","Jerry","Vans"
alert( person2.friends ); //"Tom","Jerry"
alert( person1.friends === person2.friends ); //false
在例子中,實(shí)例屬性是由構(gòu)造函數(shù)定義的,且每個(gè)實(shí)例的屬性 是獨(dú)立的。改變 實(shí)例的屬性,并不影響其它實(shí)例的屬性,這樣就避免了 原型模式的"狂熱的共享熱情"。
所有實(shí)例的共享屬性和方法 則是在原型中定義的。修改任何實(shí)例(person1或person2)中哪一個(gè),其它實(shí)例 都會(huì)受到影響。因?yàn)樗詫?shí)例的[[prototype]]指針 指向原型對(duì)象(Person.prototye),它們擁有共同的引用。構(gòu)造函數(shù)中的實(shí)例屬性 則只是 副本。
以上就是 工廠模式、構(gòu)造函數(shù)、原型模式以及組合模式 各自的特定和區(qū)別。
核心都在《JS高程3》。我做了些簡(jiǎn)單的梳理,和自己的理解。有不明白或者質(zhì)疑的地方,以書為準(zhǔn)。
總結(jié)
什么是工廠模式 ?
工廠模式就是抽象了具體對(duì)象細(xì)節(jié)過程的方法。這個(gè)方法以函數(shù)的形式封裝實(shí)現(xiàn)的細(xì)節(jié)。實(shí)現(xiàn)了重復(fù)調(diào)用的功能。
什么是構(gòu)造函數(shù)模式
構(gòu)造函數(shù)模式就是創(chuàng)建一個(gè)對(duì)象,new 這個(gè)對(duì)象就是對(duì)象的實(shí)例。實(shí)現(xiàn)重復(fù)調(diào)用的同時(shí),它的實(shí)例 也有了QQVIP的尊貴特權(quán) ,即
實(shí)例可以標(biāo)識(shí)為特定的類型。有了這個(gè)標(biāo)識(shí) 可以更好的識(shí)別出,誰是數(shù)組類型,誰是函數(shù)類型,然后你 typeof arr 或 typeof fun
一看,真的是Array類型,functiong類型。你也可以自定義自己想要的類型,這樣大大的增加了JS的拓展性。
什么是原型模式 ?
首先我們要知道,我們創(chuàng)建的每一個(gè)函數(shù)都有一個(gè)隱藏屬性,也就是 原型屬性。這個(gè)原型屬性指向一個(gè)原型對(duì)象。且所有實(shí)例和構(gòu)造函數(shù) 都指向這個(gè)原型對(duì)象,共享 原型對(duì)象的所有方法和屬性。
我們通過操作原型對(duì)象達(dá)到 實(shí)例共享屬性方法的目的,就是原型模式。
同時(shí),因?yàn)閷?shí)例都是引用 原型對(duì)象的屬性和方法,也避免了構(gòu)造函數(shù)模式下所有實(shí)例都有各自的方法的弊端。