距離上一篇部落格有一段時間了,期間各種瑣事縈繞。最主要的一件是,當我差不多將整個dojo核心原始碼看完,驚訝的發現dojo1.*的設計以是老態龍鍾之象,而我沉溺在dojo中太久,已經不知道前端世界變成了什麼樣。這無異於晴天霹靂,霹的我目瞪口呆、汗流滿面,惶惶不可終日。索性亡羊補牢為時未晚,這段期間雖有各種煩心事,但還能於百煩之中騰出點時間,看看原始碼已經是萬中之幸。各種前端類庫如浩瀚星辰,面對它們才能感覺到自身技術的淺薄,自身能力的低微。初出茅廬天下無敵,再練三年寸步難行,這就是我當前最真切的體悟。現在的我只能找幾個經典類庫,悉心研究,戒驕戒躁,誠誠懇懇的去學習大牛的程式碼,今天為大家帶來backbone的原始碼研究。能力淺薄,不足之處請各位大牛不吝斧正。
從backbone的總體結構來看,是一個立即執行的函式表示式,引數是一個匿名函式。(function(){})()和(function(){}())的目的是將函式宣告轉換為函式表示式,消除Js引擎在識別函式宣告和函式表示式上的歧義,除了小括號外還有其他運算子能夠做到,詳細介紹可以參照這篇文章:js中(function(){…})()立即執行函式寫法理解
1 2 3 4 5 |
(function(factory) { //模組定義 })(function(root, Backbone, _, $) { //Backbone }); |
模組處理內容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
function(factory) { // Establish the root object, `window` (`self`) in the browser, or `global` on the server. // We use `self` instead of `window` for `WebWorker` support. //拿到當前環境中的全域性物件;瀏覽器中為window,self也是瀏覽器提供的一個全域性物件,始終指向window //server端的執行環境則提供global這個全域性物件 var root = (typeof self == 'object' & self.self === self && self) || (typeof global == 'object' && global.global === global && global); // Set up Backbone appropriately for the environment. Start with AMD. //如果有amd載入器則將Backbone定義包裝成amd載入器可識別的模組 if (typeof define === 'function' & define.amd) { //AMD規範定義兩個全域性函式define和requrie,並且規定define有個amd屬性,來區分amd的define和普通名為define的函式 define(['underscore', 'jquery', 'exports'], function(_, $, exports) { // Export global even in AMD case in case this script is loaded with // others that may still expect a global Backbone. root.Backbone = factory(root, exports, _, $); }); // Next for Node.js or CommonJS. jQuery may not be needed as a module. //如果執行在Node端,則將Backbone包裝成CommonJs的模組 } else if (typeof exports !== 'undefined') { var _ = require('underscore'), $; try { $ = require('jquery'); } catch (e) {} factory(root, exports, _, $); // Finally, as a browser global. //以上兩種情況都沒有,則以最簡單的執行函式方式,將函式的返回值作為全域性物件Backbone } else { root.Backbone = factory(root, {}, root._, (root.jQuery || root.Zepto || root.ender || root.$)); } } |
factory部分整體結構如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
function(root, Backbone, _, $) { // Backbone.Events // --------------- // Backbone.Model // -------------- // Backbone.Collection // ------------------- // Backbone.View // ------------- // Backbone.Router // --------------- // Backbone.History // ---------------- // Helpers // ------- } |
本篇文章中,我們簡單學習兩個比較有用的工具方法:noConflict和extend。
首先介紹noConflict模式。這是jquery發明的使用方法,之後大家爭相相仿。主要原理是因為JavaScript採用的詞法作用域(通過閱讀變數定義在內的少數幾行程式碼就能知道變數的作用域),函式的作用域由定義時決定而不是由函式呼叫時決定的,所以noConflict執行時能夠訪問到previousBackbone變數。如果已經有全域性的Backbone變數,先將全域性的Backbone變數暫存在previousBackbone內,當呼叫noConflict時,全域性的Backbone指向之前暫存在previousBackbone中的Backbone,而返回的this關鍵字指向該factory函式中定義的Backbone物件。
1 2 3 4 5 6 7 8 9 10 |
// Save the previous value of the `Backbone` variable, so that it can be // restored later on, if `noConflict` is used. var previousBackbone = root.Backbone; // Runs Backbone.js in *noConflict* mode, returning the `Backbone` variable // to its previous owner. Returns a reference to this Backbone object. Backbone.noConflict = function() { root.Backbone = previousBackbone; return this; }; |
下面介紹extend方法,extend方法常見於大多數的JavaScript類庫中,來實現繼承父類創造子類。關於繼承的文章,請看我的這篇文章JavaScript物件導向之我見,這裡直接介紹原始碼了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
// Helper function to correctly set up the prototype chain for subclasses. // Similar to `goog.inherits`, but uses a hash of prototype properties and // class properties to be extended. //protoProps放置到子類原型上的屬性 //staticProps模擬靜態屬性,直接放置到子類上 var extend = function(protoProps, staticProps) { var parent = this;//利用區域性變數儲存this關鍵字 var child; // 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 constructor. //如果protoProps中有constructor屬性,則將constructor指向的函式作為建構函式 if (protoProps & _.has(protoProps, 'constructor')) { child = protoProps.constructor; } else {//沒有建構函式,則利用一個預設的函式作為建構函式。 //基本上屬於組合式繼承 child = function(){ return parent.apply(this, arguments); }; } // Add static properties to the constructor function, if supplied. //underscore中的方法,與常見的mixin函式類似 _.extend(child, parent, staticProps); // Set the prototype chain to inherit from `parent`, without calling // `parent`'s constructor function and add the prototype properties. //將child的原型鏈與parent.prototype關聯。 //_.create函式,的作用類似Object.create,第一個引數是要被繼承的原型物件,第二個引數是要混入到新物件的鍵值對 child.prototype = _.create(parent.prototype, protoProps); child.prototype.constructor = child;//原型中的constructor屬性指向child // Set a convenience property in case the parent's prototype is needed // later. child.__super__ = parent.prototype;//設定一個私有屬性指向父類的原型 return child; }; |
而後將所有Backbone對外的提供的建構函式的extend屬性都指向上文的extend函式,這樣大家都有了派生子類的功能。
1 2 |
// Set up inheritance for the model, collection, router, view and history. Model.extend = Collection.extend = Router.extend = View.extend = History.extend = extend; |
以上就是本文的主要內容,稍後將為大家帶來Model與Collection的解析。