var Tempt = Backbone.Model/.../.extend({.A...}{..B..}),
下面多處會應用上面的代碼
使用backbone的時候,不免的是各種extend,不明真相永遠是痛苦的...
痛苦是需要解決的,所以就extend得想了一些backbone的extend.
在backbone源碼的第1303行出現了傳說中的extend
Model.extend = Collection.extend = Router.extend = View.extend = extend;
根據高中和初中語文老師說的一樣,解葯往往在毒葯的旁邊,就想射雕里面過兒尋找絕情草...
對就在backbone的1298行,有下面的代碼
var extend = function(protoProps, classProps) { return inherits(this, protoProps, classProps); };
學東西,我們就應該有順藤摸瓜的精神,現在遇到的是關於inherits的理解,也就是今晚重點要理解的一個知識點,正好買的那本一塊錢一頁的書,也剛好看到這里,結合着書上的一起
理解。
廢話不多說了,先看看backbone里面是怎樣來實現inherits的
1 var inherits = function(parent, protoProps, staticProps) { 2 var child; 3 4 // The constructor function for the new subclass is either defined by you 5 // (the "constructor" property in your `extend` definition), or defaulted 6 // by us to simply call the parent's constructor. 7 if (protoProps && protoProps.hasOwnProperty('constructor')) { 8 child = protoProps.constructor; 9 } else { 10 child = function(){ parent.apply(this, arguments); }; 11 } 12 13 // Inherit class (static) properties from parent. 14 _.extend(child, parent); 15 16 // Set the prototype chain to inherit from `parent`, without calling 17 // `parent`'s constructor function. 18 ctor.prototype = parent.prototype; 19 child.prototype = new ctor(); 20 21 // Add prototype properties (instance properties) to the subclass, 22 // if supplied. 23 if (protoProps) _.extend(child.prototype, protoProps); 24 25 // Add static properties to the constructor function, if supplied. 26 if (staticProps) _.extend(child, staticProps); 27 28 // Correctly set child's `prototype.constructor`. 29 child.prototype.constructor = child; 30 31 // Set a convenience property in case the parent's prototype is needed later. 32 child.__super__ = parent.prototype; 33 34 return child; 35 };
經典的東西一般都不多,加上注釋一個35行,看看它的實現吧!
首先看它的參數部分
var inherits = function(parent, protoProps, staticProps)
其中有三個參賽分別是parent和protoProps,和staticProps,從字面上來理解,這三個參書分別代表父類,實例屬性,靜態屬性。
要理解這個函數,就必須知道三個形參對應的實參是什么,所以首先從調用它的地方看起
var extend = function(protoProps, classProps) { return inherits(this, protoProps, classProps); }; // Set up inheritance for the model, collection, and view. Model.extend = Collection.extend = Router.extend = View.extend = extend;
我們一般調用extend的時候都是通過var Tempt = Backbone.Model/.../.extend({.A...}{..B..}),所以實際上是在執行 Model.extend = function (protoProps, classProps){},然后在這個extend method中又調用並返回了 inherits(this, protoProps, classProps),根據JS精粹中關於函數5種invocation來確定this的方法,這里extend是作為Model等的method來調用的,所以這時的this應該是Model等,所以inherits形參對應的實參應該是,parent 對應的是Model/Collection/Router/View,protoProps對應的是extend({...A...}{...B...})中的A,classProps對應的是
B。帶着這樣的參數,我們繼續理解inherits函數。
接着是代碼的正文部分
// The constructor function for the new subclass is either defined by you // (the "constructor" property in your `extend` definition), or defaulted // by us to simply call the parent's constructor. if (protoProps && protoProps.hasOwnProperty('constructor')) { child = protoProps.constructor; } else { child = function(){ parent.apply(this, arguments); }; }
通過英文的注釋我們不難看出這個代碼片段完成的功能是給子類提供constructor屬性,在這里如果在extend({...A...}{...B...})中的A提供了一個constructor字段的話,這時子類的
constructor屬性將會是這個這個屬性對應的函數,如果沒有那么子類的constructor將會和父類的一樣,在這里需要復習的一個概念是child = function () {}其實是要在new的時候才會
調用的,所以我們先假設我們設置的字段里面沒有constructor,所以當我們通過var temptA = new Tempt()的時候,調用的是function () {Model.apply(temptA, arguments)},
測試見http://jsfiddle.net/xiaoxiong_1990/tErsT/ 當我們console出來的時候temptA已經擁有了一些屬性的,而這些屬性也正是
var Model = Backbone.Model = function(attributes, options) { var defaults; attributes || (attributes = {}); if (options && options.collection) this.collection = options.collection; if (options && options.parse) attributes = this.parse(attributes); if (defaults = getValue(this, 'defaults')) { attributes = _.extend({}, defaults, attributes); } this.attributes = {}; this._escapedAttributes = {}; this.cid = _.uniqueId('c'); this.changed = {}; this._silent = {}; this._pending = {}; this.set(attributes, {silent: true}); // Reset change tracking. this.changed = {}; this._silent = {}; this._pending = {}; this._previousAttributes = _.clone(this.attributes); this.initialize.apply(this, arguments); };
所定義的。
在這里個人有了第一個理論:
(1)當子類沒有提供特定的constructor的時候,應該調用父類的。
接下來接着理解inherts
// Inherit class (static) properties from parent. _.extend(child, parent);
上面雖然說只有一句代碼,但是包含的功能卻不少,通過函數的調用我們可以輕易的看出,這里實際上是調用了underscore中的extend的方法,又是extend
下面是underscore從第690行開始的
// Extend a given object with all the properties in passed-in object(s). _.extend = function(obj) { each(slice.call(arguments, 1), function(source) { for (var prop in source) { obj[prop] = source[prop]; } }); return obj; };
然后又的extend自己了,遇到的第一個問題就是關於each方法的理解,給一個測試例子http://jsfiddle.net/a6Rx4/ (這個例子為引用別人的)
var someOtherArray = ["name","patrick","d","w"]; _.each([1, 2, 3], function(num) { // In here, "this" refers to the same Array as "someOtherArray" alert( this[num] ); // num is the value from the array being iterated // so this[num] gets the item at the "num" index of // someOtherArray. }, someOtherArray);
這樣通過上面兩個代碼片段,我們就不難理解_.extend(child, parent)究竟做了什么事,我們還是把實參帶入函數來說吧
在_.extend = function (obj){...}中,obj 就是child,然后通過slice.call(arguments, 1),這樣來獲得child后面的函數,在這里就是parent,作為function (source) {...}中的source
然后通過for...in遍歷parent中的property並將其復制給child,這樣就完成了從parent復制靜態屬性到child的過程。
接着是繼承prototype的屬性,在backbone中采用了DG提出來的繼承方式(http://javascript.crockford.com/prototypal.html),即通過proxy來實現繼承,這樣就避免了在classical繼承中出現的parent的constructor被使用兩次帶來
的效率問題和在原型鏈中再次繼承this的屬性。
// Set the prototype chain to inherit from `parent`, without calling // `parent`'s constructor function. ctor.prototype = parent.prototype; child.prototype = new ctor();
在上面的例子中ctor其實是一個空的函數,它充當了parent和child的proxy.ctor的prototype屬性指向了parent的prototype屬性。child的prototype屬性是ctor這個空函數的一個實例。
通過這種方式就僅僅繼承了parent的prototype屬性。
下面的代碼就分別完成了給新的類指定實例屬性和靜態屬性的功能。
// Add prototype properties (instance properties) to the subclass, // if supplied. if (protoProps) _.extend(child.prototype, protoProps); // Add static properties to the constructor function, if supplied. if (staticProps) _.extend(child, staticProps); // Correctly set child's `prototype.constructor`. child.prototype.constructor = child; // Set a convenience property in case the parent's prototype is needed later. child.__super__ = parent.prototype;
其實上面的繼承也是JS的一種比較典型的繼承方式.....