原文見:http://coding.smashingmagazine.com/2013/08/09/backbone-js-tips-patterns/
譯者註:本文採用意譯,省略所有口水話,內容直指要義。
http://blog.csdn.net/chszs
BackboneJS是一個流行的JavaScript MVC/MVVM架構,第一版發佈於三年多前,到現在已經是一個頗有影響力的架構。儘管BackboneJS提供了JavaScript項目的基本結構,但它還留下了很多設計模式和決策供開發人員使用。
因此,本文會介紹很多不同的設計模式供BackboneJS開發過程中使用,還會介紹一些開發中可能遇到的陷阱。
應用就和建築一樣,都是遵循已知的模式進行構建。
一、實現對象的深度複製
JavaScript把所有的原生類型變數都以值傳遞的方式對待。因此,當原生類型的變數被引用時,變數的值會被傳遞。
比如,上面代碼中,設定helloWorldCopy變數的值等同於helloworld變數。因此,任何修改變數helloWorldCopy值的行為並不會影響到helloWorld變數,因為這是一份拷貝。
JavaScript把所有的非原生類型的變數都以參數傳遞的方式對待。這意味著當非原生類型的變數被引用時,JavaScript會傳遞變數的記憶體位址。
var helloWorld = { ‘hello’: ‘world’}var helloWorldCopy = helloWorld;
比如,上面的代碼設定helloWorldCopy變數的值等同於helloworld變數的引用。當修改helloWorldCopy變數時,會直接操縱helloWorld變數。而如果你想擁有一份helloWorld的拷貝,那麼你必須建立對象的拷貝。
你或許會想到這樣的問題,“為什麼BackboneJS中到處都是引用傳遞?”
BackboneJS並不會複製對象,這意味著如果你從模型調用.get()方法獲得一個對象,任何對此對象的修改都會直接修改原對象。
下面我們一起看一個例子來說明此問題。如果你有如下的Person模型:
var Person = Backbone.Model.extend({ defaults: { 'name': 'John Doe', 'address': { 'street': '1st Street', 'city': 'Austin', 'state': 'TX', 'zipCode': 78701 } }});
接著我們建立了一個新person對象:
var person = new Person({ 'name': 'Phillip W'});
接下來我們操縱新person對象的一些屬性:
person.set('name', 'Phillip W.', { validate: true });
上面的代碼成功地修改了person對象的name屬性。接著,我們試著修改person對象的address屬性。但是,在做之前,我們先驗證一下地址屬性:
var Person = Backbone.Model.extend({ validate: function(attributes) { if(isNaN(attributes.address.zipCode)) return "Address ZIP code must be a number!"; }, defaults: { 'name': 'John Doe', 'address': { 'street': '1st Street', 'city': 'Austin', 'state': 'TX', 'zipCode': 78701 } } });
接著,我們嘗試用錯誤的ZIP代碼設定地址:
var address = person.get('address');address.zipCode = 'Hello World';// Raises an error since the ZIP code is invalidperson.set('address', address, { validate: true });console.log(person.get('address'));/* Prints an object with these properties.{ 'street': '1st Street' 'city': 'Austin', 'state': 'TX' 'zipCode': 'Hello World'}*/
這會怎麼樣呢?我們的驗證出現了錯誤!為什麼屬性依舊被修改了?正如前面提過的,BackboneJS不會複製模型的屬性,它只會簡單地返回你所請求的一切。因此,如你所料,如果你請求一個對象,你會得到此對象的引用。對此對象的任何操作都會直接改變模型中的實際對象。
如果你要debug,這可能將把你帶入到無底的兔子黑洞。
實現一個深度模型對象的複製可能會讓你在調試中避免掉入兔子洞。
此問題對於BackboneJS新手要引起注意,甚至一些JavaScript老手也該提防。
在這裡:https://github.com/documentcloud/backbone/issues/2315有深度的討論。
正如Jeremy Ashkenas所說,實現深度複製是用於解決不同的問題,尤其是在很大、有深度的對象來說,此操作的記憶體開銷很大。
幸運的是,jQuery庫提過了一個深度複製的實現,即$.extend操作。順便說一句,UnderscoreJS架構(它依賴於BackboneJS),也提供了_.extend這樣的函數,但是我們應該盡量避免使用它,因為它沒有實現深度複製。
Lo-Dash (http://lodash.com/)是UnderscoreJS的分支和最佳化版本,提供了_.clone函數,實現了深度複製。但是,我使用$.extend來實現任意對象的深度複製,對象是通過.get()從模型中擷取的。記住要傳遞true,它指示執行對象的深度複製。
var address = $.extend(true, {}, person.address);
我們現在有address對象的準確複製了,我們可以隨心修改內容而無需擔心修改了實際的模型。要注意一點,此模式工作的前提是由於address對象的所有成員變數都是不可變 的(數字或字串等)。還要注意,深度複製對效能是有一點兒影響的,尤其是對於大對象來說。