摘要:于是就有了構(gòu)造函數(shù)和原型模式混合模式組合使用構(gòu)造函數(shù)模式和原型模式創(chuàng)建自定義類型最常見的方式,就是組合模式。
創(chuàng)建對象
JS有六種數(shù)據(jù)數(shù)據(jù)類型,其中五種屬于基本數(shù)據(jù)類型:Null、Boolean、undefined、String、Number。
而其它值都是對象。數(shù)組是對象,函數(shù)是對象,正則表達(dá)式是對象。對象也是對象。
來看一下對象的定義:
無序?qū)傩缘募?,其屬性可以包含基本值、對象、或者函?shù)。
我們通過對象字面量的方式 創(chuàng)建對象。創(chuàng)建的對象用于我們想要做的事。但是,如果只有對象,那么可以想象我們寫的代碼將全是光禿禿的對象,會產(chǎn)生大量的重復(fù)代碼,和命名沖突等等問題。這是非常非常糟糕的。
為了解決這個問題,人們開始使用 工廠模式的一種變體。
工廠模式抽象了具體對象的過程。也就是說,發(fā)明了一種函數(shù),把對象放到函數(shù)里,用函數(shù)封裝創(chuàng)建對象的細(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 用于檢測數(shù)據(jù)類型 var aa = [] console.log(aa instanceof Array) //true
工廠模式解決了代碼復(fù)用的問題,但是卻沒有解決對象識別的問題。即創(chuàng)建的所有實例都是Object類型。
為了解決這一問題,就有了構(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 有一個prototype(原型)屬性,這個屬性是一個指針,指向一個對象即:Person.prototype(原型對象);
實例person1 person2也有一個[[prototype]]屬性或者叫_proto_,這個屬性 也指向Person.prototype;
構(gòu)造函數(shù)、和實例 都共享Person.prototype里的 屬性和方法;
Person.prototype里有一個 constructor屬性,這個屬性也是一個指針,指向構(gòu)造函數(shù)Person。這樣以來,實例 也指向了Person,那么實例 也共享了構(gòu)造函數(shù)的屬性和方法。
構(gòu)造函數(shù)、實例、原型對象里所有的屬性和方法 都是共享的。
構(gòu)造函數(shù)解決了對象識別問題,我們在這個例子中創(chuàng)建的對所有對象既是Object的實例,同時,也是Person的實例。這一點通過instanceof操作符可以得到驗證。
console.log(person1 instanceof Object) //true console.log(person1 instanceof Person) //true
創(chuàng)建自定義的構(gòu)造函數(shù)意味著,將來可以將它的實例 標(biāo)識為一種特定類型;這正是構(gòu)造函數(shù)勝過工廠模式的地方。Array就是這種方式(我認(rèn)為的)。用構(gòu)造函數(shù)的方式,實例化一個新對象,這個對象可以是其它類型例如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ù)雖然好用,但也有缺點。既每個new出來的實例 里的方法都要重新創(chuàng)建一遍。在前面的例子中person1 person2 都有一個sayName方法,但這兩個方法不是同一個Function實例!每個實例的方法 都是不同的,不相等的。這是不合理的!
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ù)確實沒必要 每個實例,就實例一次。
于是,有需求,就有解決方案。原型模式。
我們創(chuàng)建的每個函數(shù)都有一個prototype(原型)屬性,這個屬性是一個指針,指向一個對象,而這個對象的用途是包含可以由特定類型的所有實例共享的屬性和方法。
使用原型對象的好處是可以讓所有對象實例共享它所包含的屬性和方法。也就是說,不必在構(gòu)造函數(shù)中定義對象實例的信息,而是將這些信息添加到原型對象中。
廢話少說,那么到底 原型模式是如何解決 每個實例的方法 是同一個呢?
看代碼:
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òu)造函數(shù) Person 有一個prototype(原型)屬性,這個屬性是一個指針,指向一個對象即:Person.prototype(原型對象);
實例person1 person2也有一個[[prototype]]屬性或者叫_proto_,這個屬性 也指向Person.prototype;
構(gòu)造函數(shù)、和實例 都共享Person.prototype里的 屬性和方法;
Person.prototype里有一個 constructor屬性,這個屬性也是一個指針,指向構(gòu)造函數(shù)Person。這樣以來,實例 也指向了Person,那么實例 也共享了構(gòu)造函數(shù)的屬性和方法。
構(gòu)造函數(shù)、實例、原型對象里所有的屬性和方法 都是共享的。
與構(gòu)造函數(shù)相比,
原型模式,把公共方法提出來放到prototype對象里。
每個實例 的[[prototype]]指針 指向這個對象,所以所有實例的公共方法 是同一個。這樣也避免了內(nèi)存浪費(fèi)。
略
詳情參見 《JS高程3》 第六章
原型模式的問題我們寫代碼的時候,很少只用到原型模式,說明它還是有坑的。
原型模式很大的優(yōu)點 是所有實例都共享屬性和方法。這個很好的優(yōu)點 對于函數(shù)來說非常合適,可對于屬性來說,有點不適應(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)很明了了,給一個實例添加屬性值,結(jié)果,所有實例的屬性值也改變了。實例應(yīng)該具有自己的獨(dú)立性。自己莫名其妙的被改變,肯定是有問題的。
有問題,就會有解決方案,再多坑,也抵不過我們前輩的不懈努力。
于是就有了 構(gòu)造函數(shù)和原型模式混合模式
創(chuàng)建自定義類型最常見的方式,就是組合模式。
構(gòu)造函數(shù)模式用于定義實例屬性,而原型模式用于定義方法和共享的屬性。
結(jié)果,每個實例都有自己的一份實例屬性的副本。注意是是 副本。同時,又共享著對方法的引用,最大限度地節(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
在例子中,實例屬性是由構(gòu)造函數(shù)定義的,且每個實例的屬性 是獨(dú)立的。改變 實例的屬性,并不影響其它實例的屬性,這樣就避免了 原型模式的"狂熱的共享熱情"。
所有實例的共享屬性和方法 則是在原型中定義的。修改任何實例(person1或person2)中哪一個,其它實例 都會受到影響。因為所以實例的[[prototype]]指針 指向原型對象(Person.prototye),它們擁有共同的引用。構(gòu)造函數(shù)中的實例屬性 則只是 副本。
以上就是 工廠模式、構(gòu)造函數(shù)、原型模式以及組合模式 各自的特定和區(qū)別。
核心都在《JS高程3》。我做了些簡單的梳理,和自己的理解。有不明白或者質(zhì)疑的地方,以書為準(zhǔn)。
工廠模式就是抽象了具體對象細(xì)節(jié)過程的方法。這個方法以函數(shù)的形式封裝實現(xiàn)的細(xì)節(jié)。實現(xiàn)了重復(fù)調(diào)用的功能。什么是構(gòu)造函數(shù)模式 ?
構(gòu)造函數(shù)模式就是創(chuàng)建一個對象,new 這個對象就是對象的實例。實現(xiàn)重復(fù)調(diào)用的同時,它的實例 也有了QQVIP的尊貴特權(quán) ,即什么是原型模式 ?
實例可以標(biāo)識為特定的類型。有了這個標(biāo)識 可以更好的識別出,誰是數(shù)組類型,誰是函數(shù)類型,然后你 typeof arr 或 typeof fun
一看,真的是Array類型,functiong類型。你也可以自定義自己想要的類型,這樣大大的增加了JS的拓展性。
首先我們要知道,我們創(chuàng)建的每一個函數(shù)都有一個隱藏屬性,也就是 原型屬性。這個原型屬性指向一個原型對象。且所有實例和構(gòu)造函數(shù) 都指向這個原型對象,共享 原型對象的所有方法和屬性。
我們通過操作原型對象達(dá)到 實例共享屬性方法的目的,就是原型模式。
同時,因為實例都是引用 原型對象的屬性和方法,也避免了構(gòu)造函數(shù)模式下所有實例都有各自的方法的弊端。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/107641.html
摘要:創(chuàng)建對象在之前說過通過構(gòu)造函數(shù)或者對象字面量的方式可以創(chuàng)建對象。寄生構(gòu)造函數(shù)模式寄生構(gòu)造函數(shù)模式的基本思想是創(chuàng)建一個函數(shù),該函數(shù)的作用僅僅是封裝創(chuàng)建對象的代碼,然后在返回新創(chuàng)建的對象。 創(chuàng)建對象 在之前說過通過Object構(gòu)造函數(shù)或者對象字面量的方式可以創(chuàng)建對象。但是這些方式有一個明顯的問題,使用同一個接口創(chuàng)建很多對象會產(chǎn)生大量的重復(fù)代碼。例如: //如果你要創(chuàng)建三個對象,通過Obje...
摘要:將構(gòu)造函數(shù)的作用域賦值給新的對象因此指向了這個新對象。以這種方式定義的構(gòu)造函數(shù)是定義在對象在瀏覽器是對象中的。構(gòu)造函數(shù)在不返回值的情況下,默認(rèn)會返回新對象實例。在創(chuàng)建子類型的實例時,不能向超類型的構(gòu)造函數(shù)中傳遞參數(shù)。 創(chuàng)建對象 雖然Object構(gòu)造函數(shù)或?qū)ο笞置媪慷伎梢杂脕韯?chuàng)建單個對象,但是這些方式有明顯的缺點:使用同一個接口創(chuàng)建很多對象,會產(chǎn)生大量的重復(fù)代碼。為解決這個問題,人們開始...
摘要:在基于原型的面向?qū)ο蠓绞街?,對象則是依靠構(gòu)造函數(shù)和原型構(gòu)造出來的。來看下面的例子優(yōu)點與單純使用構(gòu)造函數(shù)不一樣,原型對象中的方法不會在實例中重新創(chuàng)建一次,節(jié)約內(nèi)存。 我們所熟知的面向?qū)ο笳Z言如 C++、Java 都有類的的概念,類是實例的類型模板,比如Student表示學(xué)生這種類型,而不表示任何具體的某個學(xué)生,而實例就是根據(jù)這個類型創(chuàng)建的一個具體的對象,比如zhangsan、lisi,由...
摘要:構(gòu)造函數(shù)模式定義構(gòu)造函數(shù)模式是語言創(chuàng)建對象的通用方式。但兩種語言用構(gòu)造函數(shù)創(chuàng)建對象的方式略有不同在中沒有類的概念,函數(shù)即為一等公民,因此,不必顯式聲明某個類,直接創(chuàng)建構(gòu)造函數(shù)即可,類的方法和屬性在構(gòu)造函數(shù)中或原型對象上處理。 工廠模式 定義:工廠模式非常直觀,將創(chuàng)建對象的過程抽象為一個函數(shù),用函數(shù)封裝以特定接口創(chuàng)建對象的細(xì)節(jié)。通俗地講,工廠模式就是將創(chuàng)建對象的語句放在一個函數(shù)里,通...
閱讀 2168·2021-10-08 10:15
閱讀 1197·2019-08-30 15:52
閱讀 524·2019-08-30 12:54
閱讀 1542·2019-08-29 15:10
閱讀 2695·2019-08-29 12:44
閱讀 3017·2019-08-29 12:28
閱讀 3365·2019-08-27 10:57
閱讀 2224·2019-08-26 12:24