詳解 javascript物件建立模式
建立模式
在javascript中,主要有以下幾種建立模式:
工廠模式
建構函式模式
原型模式
組合模式
動態原型模式
寄生建構函式模式
穩妥構造模式
工廠模式
工廠模式是軟體工程領域一種廣為人知的設計模式。javascript實現方式:
function createPerson(name,obj,job) { var o = new Object(); o.name = name; o.obj = obj; o.job = job; o.sayName = function() { alert(this.name); } return o; } var person1 = createPerson("Nicholas",29,"software Enginner"); var person2 = createPerson("Greg",27,"Doctor");
工廠模式雖然解決了建立多個相似物件的問題,但卻沒有解決物件識別問題
建構函式模式
function Person(name,age,job) { this.name = name; this.age = name; this.job = name; this.sayName = function () { alert(this.name); } } var person1 = new Person("Nicholas","Software Engineer"); var person2 = new Person("Greg","Doctor"); person1 instanceof Person; // true person1 instanceof Object; // true person2 instanceof Person; // true person2 instanceof Object; // true
new操作符實現原理請檢視文章附錄
不同於工廠模式,建構函式模式
沒有顯示建立物件
直接將屬性和方法賦值給了this物件
沒有return語句
解決了物件識別問題
但是建構函式模式同樣存在問題,就是每個方法都要在每個例項上重新申明一遍。person1和person2都有一個名為 sayName() 的方法,但那兩個方法不是同一個Function例項。(在javascript中,函式實質上也是物件,因此每定義一個函式,也就是例項化一個物件。)
通過吧函式定義轉移到建構函式外部可以解決這個問題:
function Person(name,job) { this.name = name; this.age = age; this.job = job; this.sayName = sayName; } function sayName() { alert(this.name); } var person1 = new Person("Nicholas","Doctor");
但這種方式又帶來了一個新的問題,我們在全域性建立了一個全域性函式。
需要注意一點,按照慣例,建構函式始終應該以一個大寫字母開頭,而非建構函式應該以一個小寫字母開頭。這主要用於區別建構函式和非建構函式,因為建構函式本身也是函式。
原型模式
我們建立的每個函式都有一個 prototype (原型)屬性,這個屬性是一個指標,指向一個物件,而這個物件的用途可以由特定型別的所有例項共享的屬性和方法。
函式原型物件請檢視附錄
通過原型模式建立物件,我們不必在建構函式中定義物件例項的資訊,同時例項化多個物件,每個物件不會再申明一個新的函式。
可以看到, person1.sayName 和 person2.sayName 都指向了同一個函式。
但是原型模式的缺點也是顯而易見的。
首先原型模式省略了建構函式模式傳遞引數這一環節,結果導致所有例項的初始值在預設情況下都是相同的屬性值。
更重要的是,因為將屬性和方法都放置在原型物件上,實質上原型上的屬性是 被所有例項所共享的 。對於包含基本值的屬性還表現正常,改變屬性值,只是在例項上新增一個同名屬性。但對於引用型別值的屬性來說,這可能是個災難。
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"); person1.friends; // ["shelby","Court","Van"] person2.friends; // ["shelby","Van"]
組合模式
建立自定義型別最常見的方式,就是組合使用建構函式模式和原型模式。構造模式用於定義例項屬性,而原型模式用於定義方法和共享屬性。
這樣,每個例項都會有自己的一份例項屬性副本,但同時又共享方法的引用。
function Person(name,job) { this.name = name; this.age = age; this.job = job; this.friends = ["Shelby","Court"]; } Person.prototype.sayName = function() { alert(this.name); } var person1 = new Person("Nicholas","Software Enginner"); var person2 = new Person("Greg","Doctor"); person1.friends.push("Van"); person1.firends; // ["Shelby","Van"]; person2.firends; // ["Shelby","Court"] person1.firends === person2.firends; // false person1.sayName === person2.sayName; // true
動態原型模式
function Person(name,job) { this.name = name; this.age = age; this.job = job; if (typeof this.sayName != "function") { Person.prototype.sayName = function() { alert(this.name); } } }
寄生建構函式模式
寄生模式的基本概念就是建立一個函式,該函式的作用僅僅是封裝建立物件的程式碼,然後再返回新建立的物件。
function Person(name,job) { var o = new Object(); o.name = name; o.age = age; o.job = job. o.sayName = function() { alert(this.name); } } var person1 = new Person("Nicholas","Software Engineer"); person1.sayName(); // "Nicholas"
看起來,除了使用new操作符之外,這個模式和工廠模式其實是一模一樣的。
這個模式可以在特殊的情況下用來作為物件建立建構函式。
假設我們需要一個具有額外方法的特殊陣列型別。由於不能直接修改Array建構函式,因此可以使用這個模式。
function SpecialArray() { var values = []; values.push.push(values,arguments); values.toPipedString = function() { return this.join("|"); } return values; } var colors = new SpecialArray("red","blue","green"); colors.toPipedString(); // "red|blue|green"
該模式主要缺點:
返回的物件和建構函式或建構函式的原型屬性間沒有關係,不·能依賴instanceof來確定物件型別。
在其他模式能夠使用的情況下,儘量不要使用這種模式。
穩妥建構函式模式
function Person(name,job) { var o = new Object(); var name = name; var age = age; var job = jbo; o.sayName = function() { alert(name); } } var person1 = Person("Nicholas","Software Enginner"); firend.sayName(); // "Nicholas"
- 附錄
- new 操作符
new操作符實際上會經歷4個步驟:
- 建立一個空的簡單JavaScript物件(即**{}**);
- 連結該物件(設定該物件的constructor)到另一個物件 ;
- 將步驟1新建立的物件作為**this**的上下文 ;
- 如果該函式沒有返回物件,則返回**this**。
function new(func) { var o = {}; o.__proto__ = func.prototype; var result = func.apply(o,arguments); return typeof result === "object" ? object : o; }
函式原型物件
理解原型物件
無論什麼時候,只要建立一個新函式,就會根據一組特定的規則為該函式建立一個prototype屬性,這個屬性指向函式的原型物件。在預設情況下,所有原型物件都會自動獲得一個construtor(建構函式)屬性,這個屬性包含一個指向prototype屬性所在函式的指標。
在建立了一個自定義的建構函式之後,其原型物件只會取得construtoe屬性,至於其他屬性,則都是從Object繼承而來。當呼叫建構函式建立一個新例項時,該例項的內部將包含一個指標(內部屬性),指向建構函式的原型物件,這個指標叫[[Prototype]]。在多數瀏覽器中,每個物件都支援一個屬性__proto__來呼叫[[Prototype]]。
雖然所有實現都無法直接訪問[[Prototype]],但可以通過isPrototype方法來確定物件之間是否存在關係。
我們測試了person1和person2,都返回了true。因為他們內部都有一個指向Person.prototype的指標。
Object.getPrototype()可以返回物件的原型物件。
每當程式碼讀取某個物件的屬性時,都會執行一次搜尋,目標是具有給定名字的屬性。搜尋首先會從物件本身開始,如果在例項中找到了對應的屬性,則返回該屬性的值。如果沒找到,則繼續搜尋指標指向的原型物件。這也是為什麼我們在person1和person2兩個例項中,並沒有定義sayName這個屬性,但仍能夠正常使用。
我們在呼叫person1.sayName()是,會執行兩次搜尋。首先,解析器會問:“例項person1有sayName屬性嗎?”,答:“沒有”。然後他繼續搜尋,再問:“person1的原型有sayName屬性嗎?”,答:“有”。於是,它就讀取儲存在原型中的函式。
雖然我們能夠通過例項訪問原型的屬性,但卻不能重新原型的屬性。
如果我們在例項上新增屬性名,而這個屬性名又與原型中的屬性名相同,即我們希望在例項中重寫屬性。
function Person() {} Person.prototype.name = 'Nicholas'; var person1 = new Person(); var person2 = new Person(); person1.name === person2.name; // true person1.name = 'Greg'; person1.name === person2.name; // false person1.name; // 'Greg' person2.name; // 'Nicholas' person1.__proto__.name; // 'Nicholas'
事實上,當我們重寫原型屬性時,只是在例項上添加了一個新屬性。當我們把例項上的屬性刪除後,又會暴露出原型屬性。
delete person1.name; person1.name; // 'Nicholas'
使用hasOwnProperty()函式能判斷屬性是否在例項上。
person1.hasOwnProperty('name'); // false person1.name = 'Greg'; person1.hasOwnProperty('name'); // true
以上就是詳解 javascript物件建立模式的詳細內容,更多關於Java 建立模式的資料請關注我們其它相關文章!