Backbone建構函式
我之所以說Backbone很簡單,是因為Backbone只有四個建構函式能被典型的執行個體化(基本上,它們首先被繼承或子分類)。這四種建構函式包括:
- Backbone.Model = function(attributes, {options}){};
- Backbone.Collection = function([models], {options}){};
- Backbone.Router = function({options}){}; //只能被具現化一次
- Backbone.View = function({options}){};
現在我們來看看這些建構函式建立的每個執行個體中預設執行個體和原型屬性是怎樣的,簡單用new關鍵詞實現具現化,且不傳遞參數。開啟瀏覽器控制台,你可以更清晰的查看執行個體和原型屬性。
模型
- var myModel =newBackbone.Model();
-
- console.log('myModel = new Backbone.Model();');
-
- console.log('myModel Instance Properties:');
- console.log(_.keys(myModel));
-
- console.log('myModel Prototype Properties & Methods:');
- console.log(_.keys(Object.getPrototypeOf(myModel)));
集合
- var myCollection =newBackbone.Collection();
-
- console.log('var myCollection = new Backbone.Collection();');
-
- console.log('myCollection Instance Properties:');
- console.log(_.keys(myCollection));
-
- console.log('myCollection prototype Properties & Methods:');
- console.log(_.keys(Object.getPrototypeOf(myCollection)));
路由器
- var myRouter =newBackbone.Router({routes:{'foo':'bar'},bar:function(){}});
-
- console.log('var myRouter = new Backbone.Router();');
-
- console.log('myRouter Instance Properties:');
- console.log(_.keys(myRouter));
-
- console.log('myRouter prototype Properties & Methods:');
- console.log(_.keys(Object.getPrototypeOf(myRouter)));
視圖
- var myView =newBackbone.View();
-
- console.log('var myView = new Backbone.View();');
-
- console.log('myView Instance Properties:');
- console.log(_.keys(myView));
-
- console.log('myView Inherited Properties & Methods:');
- console.log(_.keys(Object.getPrototypeOf(myView)));
每種建構函式的屬性均由Backbone現成提供。但是,通常你在構造這些函數之前,需要讓它們繼承(Backbone.Model.extend())你自己的域特定的執行個體、原型屬性和方法。
在繼續下面的小節之前,我們簡單的來看一下每種建構函式的選擇性參數項。本章中有一節專門詳細敘述了這些參數,不過現在可以大致的瞭解一下選擇性參數項的概念。
- var myModel =newBackbone.Model(attributes,{options});
- /*
- attributes = {data:value, data:value} [value,value]
-
- options = {
- collection: {},
- url: '',
- urlRoot: '',
- parse: boolean
- }
- */
-
-
- var myCollection =newBackbone.Collection(model(s),{options});
- /*
- models = model [model,model,model] {model:data} [{model:data},{model:data}];
-
- options = {
- url: '',
- model: {},
- comparator: function(){} ''
- }
- */
-
-
- var myView =newBackbone.View({options});
- /*
- options = {
- model: {},
- events: {} or function(){return {}}
- collection: {},
- el: '' or function(){return ''},
- id: '',
- className: '' or function(){return ''},
- tagName: '' or function(){return ''},
- attributes: {attribute:value,attribute:value}
- }
- */
-
-
- var myRouter =newBackbone.Router({options});
- /*
- options = {
- routes:{}
- }
- */
-
- /*還要注意曆史選項*/
- Backbone.History.start({options});
- /*
- options = {
- pushState: boolean,
- root: '',
- hashChange: boolean,
- silent: boolean
- }
- */
提示:
- Router()建構函式能夠被具現化的次數超過一次,但是,當它被給定了一個SPA的時候,通常只能被具現化一次。
- Backbone.History()建構函式是由Backbone庫本身內部構造的,當這個庫被解析的時候。
- 請注意,正如前文提到的,所有執行個體中每個建構函式的原型都含有Backbone.Events。
利用內部Backbone extend()輔助函數建立子類Model()、Collection()、Router()和View()建構函式
用Backbone模式構建一個模型、集合、視頻或路由器,經常涉及到讓一個預設的Backbone建構函式繼承(建立一個子類或子建構函式)你自己的域相關的屬性和方法。這個步驟利用了extend()輔助函數,它是一個屬性的建構函式。不要把這個函數與underscore.js中的extend()混淆了。
總體來說,Backbone中的extend()可以完成以下功能:
- 複製建構函式繼承的執行個體值
- 讓新的子建構函式繼承開發人員定義的原型屬性和靜態屬性
- 促進新的建構函式(子建構函式)與被繼承的建構函式之間的原型繼承
- 添加extend()輔助函數到新的子建構函式,新的子建構函式同樣能夠被繼承,以便建立子類的連鎖鏈
- 建立一個initialize()回呼函數,能被子建構函式建立執行個體時調用,並設定初始化函數的上下文
有關更多Backbone extend()方法如何工作的詳細內容,請仔細閱讀下面的代碼,詳述了利用extend()函數繼承一個虛構的View建構函式(與Backbone.View類似但不盡相同)時的邏輯。這段代碼不完全是Backbone.js中包含的準備代碼,而是一個獨立的示範,從代碼層講述當Backbone.Model, Backbone.Collection, Backbone.View和Backbone.Router被子分類(我更習慣叫做一個擴充的子建構函式)時發生了什麼變化。
- /* 建立一個視圖建構函式,有點類似Backbone.js中的Backbone.View */
- varView=function(parameter1,parameter2){
- this.IAm='View Constructor';
- /*調用原型鏈中的第一個初始化函數,並設定它的範圍到this*/
- this.initialize.apply(this, arguments);//傳遞參數數組到初始化函數
- };
-
- /*當無初始化函數被傳遞給extend()函數, 調用一個空的初始化函數來繼承這個視圖建構函式的原型*/
- View.prototype.initialize =function(){};
-
- /*建立一個通用靜態extend()函數,用於建立子類(子類建構函式),把它視作一個屬性視圖建構函式*/
- View.extend =function(protoProps, staticProps){
-
- var parent =this;/*代碼中的this是一個視圖建構函式,也就是說函數繼承是一個靜態屬性*/
- var child;/*this將成為子類建構函式,在代碼中特別顯示為subView*/
-
- if(protoProps && _.has(protoProps,'constructor')){/*檢查是否有被傳遞給extend()一個可替換的建構函式*/
- child = protoProps.constructor;/*如果有,則將它作為子建構函式*/
- }else{
- /*如果沒有,則創造一個函數(也就是子建構函式),可以調用View()建構函式把子建構函式的參數傳遞給它,
- 並設定文本到子建構函式。這個步驟複製了View建構函式的屬性*/
-
- child =function(){
- return parent.apply(this, arguments);
- };
- }
- /*添加靜態父類屬性以及任何用於extend()的靜態屬性,當然父類中包含了extend()它本身,
- 因此subView()現在能調用extend()和子構造並且可以用於持續下去*/
- _.extend(child, parent, staticProps);
-
- /*讓原型鏈繼承父類(代碼中的View), 但不調用父類建構函式
- 基本上,thsi串連父類建構函式(代碼中的View)與子建構函式之間的原型鏈*/
- varSurrogate=function(){
- this.constructor = child;
- };
- Surrogate.prototype = parent.prototype;
- child.prototype =newSurrogate();
-
- /*用if給子建構函式添加原型屬性,所有子建構函式的執行個體都將繼承這些屬性 */
- if(protoProps){ _.extend(child.prototype, protoProps);}
-
- /*令子建構函式的靜態屬性,指向父類原型(View.prototype*/
- child.__super__ = parent.prototype;
-
- return child;/*返回子建構函式,全部準備好被調用來繼承父類*/
- };
- /*繼承View,基於View建構函式建立一個子建構函式(也就是繼承View建構函式,令subView繼承View)*/
- var subView =View.extend({
- protoPropsHere:'protoProps',
- Iam:'subView Constructor',
- initialize:function(parameter1,parameter2){//當subView建立了一個執行個體,運行初始化
- console.log('sub-constructor has been used, instance created');
- console.log('The '+ parameter1 +' '+ parameter2 +' have been passed');
- console.log('the value of this:'+this.Iam+', was correctly set');
- }
- },{
- staticePropertiesHere:'staticProperties'
- }
-
- );
-
- /*驗證subView的靜態屬性*/
- console.log(subView.staticePropertiesHere);
-
- /*建立subView執行個體,命名為myView,運行初始化函數傳遞可選項*/
- var myView =new subView('param1','param2');
-
- /*驗證myView的原型屬性*/
- console.log(myView.protoPropsHere);
理解extend()的概念以及它的作用,就能弄清楚Backbone應用程式的原理。我建議你反覆測試這個部分,直到你完全瞭解extend()的功能和作用。如果你想快速跳過這個部分,我將本小節總結為以下這點:
- extend()能建立或繼承新應用程式特定的對象屬性,從而使繼承的對象執行個體化之後將擁有這些新的執行個體功能與方法。
當被擴充的建構函式被執行個體化,運行初始化代碼
當使用extend()擴充Backbone.Model, Backbone.Collection, Backbone.View以及Backbone.Router一個初始化函數,可以設定隨時運行由被繼承的建構函式建立的一個執行個體。基本上,初始化函數是一個回呼函數,當一個執行個體被初始化時就被隨時調用。在以下代碼中,我提供了一個被繼承的Backbone.Model建構函式的初始化回呼函數,然後建立一個執行個體,依次運行初始化函數(在Backbone.Collection, Backbone.View和Backbone.Router中同樣適用)。
- var myModel =Backbone.Model.extend({
- /*the context of the initizlie function is set
- to the instance object being created.*/
- //log the properties in this/instance
- initialize:function(){
- console.log(_.keys(this));
- console.log(_.keys(this.constructor.prototype));//where you'll find name
- },
- name:'myModel'
- });
-
- //建立一個執行個體,驗證初始化被調用
- new myModel;
請注意,所有傳遞給被繼承的建構函式的參數同時也被傳遞給了初始化回呼函數。
- var myModel =Backbone.Model.extend({
- initialize:function(a,b,number){console.log(a,b,number)},//log paramaters
- });
- new myModel({a:'a'},{b:'b'},1);//傳遞屬性,可選項和隨機參數
提示:
- 初始化函數(this)的文本被設定給所建立的執行個體。
- 在繼承一個Backbone建構函式時,你應該注意這個建構函式在初始化一個執行個體時需要的是什麼參數和什麼值。比如,Backbone.Model建構函式想要的第一個參數是包含在模型中儲存資料的對象(也就是屬性)。第二個參數是包含初始化選項的對象。你可以添加附加的參數,簡單傳遞給初始化函數。
重載被繼承的建構函式
當使用extend()時,你把一個稱作'constructor' 的屬性作為值傳遞到一個函數,那麼這個函數就會被替換成 extend()的屬性。在下面代碼中,我繼承的不是Model建構函式,而是繼承了我所傳遞的建構函式。
- var subModel =Backbone.Model.extend({
- constructor:function(){//extend this constructor, not Model
- this.foo ='bar';
- }
- });
-
- myModel =new subModel();
-
- console.log(myModel.foo);//日誌 bar
-
- /*cid由Model建構函式設定的一個執行個體屬性,但是日誌未定義
- 因為我們建立了我們自己的建構函式*/
- console.log(myModel.cid);
-
- /*cid屬性是由Model()設定的一個執行個體屬性,我們沒有使用Model
- 我們使用的是一個自訂的建構函式,所以myModel沒有cid屬性*/
在下面的代碼中,myModel依舊繼承了Model()原型屬性,而不是執行個體屬性(cid)。如果你重載這個建構函式,並希望保留建構函式的執行個體屬性,你可以調用extend(),你需要在自訂的建構函式運行Backbone.[CONSTRUCTOR].apply(this, arguments)。
- var subModel =Backbone.Model.extend({
- constructor:function(){//繼承這個建構函式,而不是Model
- this.goo ='boo';
- Backbone.Model.apply(this, arguments);//添加Model執行個體屬性
- }
- });
-
- myModel =new subModel();
-
- console.log(myModel.goo);//日誌 boo
- console.log(myModel.cid);//日誌 c1, cid是一個Model執行個體屬性