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