天天看點

Backbone源碼分析(一)

  距離上一篇部落格有一段時間了,期間各種瑣事萦繞。最主要的一件是,當我差不多将整個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的解析。

繼續閱讀