距離上一篇部落格有一段時間了,期間各種瑣事萦繞。最主要的一件是,當我差不多将整個dojo核心源碼看完,驚訝的發現dojo1.*的設計以是老态龍鐘之象,而我沉溺在dojo中太久,已經不知道前端世界變成了什麼樣。這無異于晴天霹靂,霹的我目瞪口呆、汗流滿面,惶惶不可終日。索性亡羊補牢為時未晚,這段期間雖有各種煩心事,但還能于百煩之中騰出點時間,看看源碼已經是萬中之幸。各種前端類庫如浩瀚星辰,面對它們才能感覺到自身技術的淺薄,自身能力的低微。初出茅廬天下無敵,再練三年寸步難行,這就是我目前最真切的體悟。現在的我隻能找幾個經典類庫,悉心研究,戒驕戒躁,誠誠懇懇的去學習大牛的代碼,今天為大家帶來backbone的源碼研究。能力淺薄,不足之處請各位大牛不吝斧正。
從backbone的總體結構來看,是一個立即執行的函數表達式,參數是一個匿名函數。(function(){})()和(function(){}())的目的是将函數聲明轉換為函數表達式,消除Js引擎在識别函數聲明和函數表達式上的歧義,除了小括号外還有其他運算符能夠做到,詳細介紹可以參照這篇文章:js中(function(){…})()立即執行函數寫法了解
(function(factory) {
//子產品定義
})(function(root, Backbone, _, $) {
//Backbone
});
子產品處理内容如下:
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部分整體結構如下:
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對象。
// 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面向對象之我見,這裡直接介紹源碼了。
// 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函數,這樣大家都有了派生子類的功能。
// Set up inheritance for the model, collection, router, view and history.
Model.extend = Collection.extend = Router.extend = View.extend = History.extend = extend;
以上就是本文的主要内容,稍後将為大家帶來Model與Collection的解析。