JS基礎篇-underscore源碼解析

總想找個機會夯實一下本身的JS基礎,正好最近略有悠閒,看視頻?讀書?擼代碼?我選擇了第三者(怎麼感受有點彆扭),看視頻的話效率不高適合入門,看書的話,一本《你不知道的JavaScript》推薦給你們,選擇繼續看書的話仍是算了吧,畢竟讀萬卷書不如行萬里路是吧。擼代碼的話,擼個項目?本身雜七雜八寫了很多了,都是老套路難有新的突破,想一想倒不如研究研究大神的代碼,學習學習大神的結構設計,技術細節等站在大神的肩膀上是吧。在選擇是翻譯Loadsh或Underscore的時候我更偏向於前者,由於念起來比較順嘴,但當我看到Loadsh源碼的時候發現,這個!這個!我發現「安得兒私購」其實也挺順嘴的,1600多行,API豐富,結構清晰,就你了。最後在補充幾句,看源碼的感受有點像「春天的雨,潤物細無聲」,沒感受本身技術有多大提升,但當看到其餘類庫的時候已經知其大概結構,當要本身實現_.flatten或_.chain的時候已經有了思路,也算是有所進步吧。此分析文章會持續更新,若有錯誤感謝提出!
原文地址css

//參考文檔:http://underscorejs.org/
//參考文檔:http://www.bootcss.com/p/underscore/
/*
  建議:
      1 剛開始不要一行一行跟下來敲,先了解一下庫的總體結構
      2 遇到不懂的打個斷點,多跟蹤幾遍,斷點很重要
      3 有些內部函數使用頻率很高(cb....),這些內部函數了解清楚了後續看起來輕鬆很多
      4 有些函數內,有較多的函數引用,建議多讀幾遍

*/
(function() {
  //獲取根對象,瀏覽器是window(self),Node是global,window.window===window返回true,global亦然
  var root = typeof self == 'object' && self.self === self && self ||
            typeof global == 'object' && global.global === global && global ||
            this ||
            {};
  //獲取現有的_對象,避免衝突,具體解決衝突方法後面會說
  var previousUnderscore = root._;
  //獲取原型對象,爲的是寫起來方便不用每次都xxx.proto...一大推
  var ArrayProto = Array.prototype, ObjProto = Object.prototype;
  var SymbolProto = typeof Symbol !== 'undefined' ? Symbol.prototype : null;

  var push = ArrayProto.push,
      slice = ArrayProto.slice,
      toString = ObjProto.toString,
      hasOwnProperty = ObjProto.hasOwnProperty;

  var nativeIsArray = Array.isArray,
      nativeKeys = Object.keys,
      nativeCreate = Object.create;
  //中轉函數,後面用到會說
  var Ctor = function(){};
  /*
  _的構造函數,
  第一步是看看obj是不是_的實例,若是是就不操做直接返回,有點像$($("#d1")),jq或zepto裏也有類似的判斷
  第二步是判斷this是不是_的實例,不是則進行new 調用,
  注意,在進行new調用的時候 new會作4件事,1建立空對象,2空對象的__proto__指向函數的prototype,3this指向空對象(此時可能會添加屬性等),4判斷返回值,
  而此時在當前的_裏this已經指向空對象
  第三步爲當前對象添加_wrapped屬性,這是爲了後面的鏈式調用作準備
  */
  var _ = function(obj) {
    if (obj instanceof _) return obj;
    if (!(this instanceof _)) return new _(obj);
    this._wrapped = obj;
  };

  //根據當前環境添加_對象
  if (typeof exports != 'undefined' && !exports.nodeType) {
    if (typeof module != 'undefined' && !module.nodeType && module.exports) {
      exports = module.exports = _;
    }
    exports._ = _;
  }
   else {
    root._ = _;
  }

  // 版本號
  _.VERSION = '1.8.3';

  //下面就是一些經常使用的方法了,後面會一點一點分析
  // _.each=_.forEach=function(){......}
  //...
  //...
  //...


  /*
  鏈式函數
  實例化當前對象,設置_china爲true,此爲判斷鏈式調用屬性,true爲鏈式調用
  */
  _.chain = function(obj) {
    var instance = _(obj);
    instance._chain = true;
    return instance;
  };

  //判斷是否繼續鏈式調用
  var chainResult = function(instance, obj) {
    return instance._chain ? _(obj).chain() : obj;
  };

  /*
  擴展_的方法 
  第一步遍歷obj裏所含方法,執行回調
  回調內  
      1獲取obj的function,擴展到_裏,並保存到func
      2對_的prototype進行擴展,擴展函數裏進行取值添加等操做(注意this指向),最後執行func.apply(_, args)(注意apply還有打散數組的功能)把結果和this做爲參數傳遞到chainResult中,判斷是否繼續鏈式調用
  第二步 返回_
  
  最後在解釋一下爲何_.prototype[name]=function(){....},若是理解請跳過此段
  你們通常都是_.filter({name:"Mr.zhou"},function(){.....})
  鏈式調用說白了就是將第一個方法的執行結果做爲參數傳到第二個方法裏,如此依次傳遞,直到最後一個返回結果;
  想要鏈式調用經常使用的_.filter(...)的方法確定是不行了,具體實現請看例子
  var stooges = [{name: 'curly', age: 25}, {name: 'moe', age: 21}, {name: 'larry', age: 23}];
  var youngest = _.chain(stooges)
                    .sortBy(function(stooge){ return stooge.age; })
                    .value();
  1 建立stooges對象
  2 建立youngest變量
  3 詳細看一下youngest值的計算方法
    3.1 先是_.chain(stooges)這句話作了什麼呢?(能夠回顧一下以前的代碼)
      調用_.chain(stooges),內部對_進行實例化,並把stooges做爲_wrapped的值,並添加了一個名爲_chain值爲true的屬性,
      最後獲得的就是這樣一個對象{_wrapped:[{name: 'curly', age: 25}...],_chain:true}
    3.2 繼續調用
      {_wrapped:[{name: 'curly', age: 25}...],_chain:true}.sortBy(function(stooge){ return stooge.age; })
                                                          .value();
      等等,這樣對嗎?內個什麼對象調用.sortBy不報錯嗎?它有這個方法嗎?
      是有的,你沒聽錯,那麼在哪裏呢?
      請看_.mixin的這句換_.prototype[name]=function(){....}
      這句話就是在往_的原型對象中添加方法,在這句話以前的_.mixin(_),與其內部的_.each(_.function(obj),...)就是將_上面的全部方法的地址引用傳遞給_.prototype上,而{_wrapped:[{name: 'curly', age: 25}...],_chain:true}對象又是_的實例對象,天然也就繼承了_.prototype的方法,這也就是鏈式調用的原理
    3.3 最後調用value()來返回它的_wrapped就此結束
    */
  _.mixin = function(obj) {
    _.each(_.functions(obj), function(name) {
      var func = _[name] = obj[name];
      _.prototype[name] = function() {
        var args = [this._wrapped];
        push.apply(args, arguments);
        return chainResult(this, func.apply(_, args));
      };
    });
    return _;
  };

  //自調mixin並把_傳入
  _.mixin(_);

  // 同mixin差很少添加方法
  _.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
    var method = ArrayProto[name];
    _.prototype[name] = function() {
      var obj = this._wrapped;
      method.apply(obj, arguments);
      if ((name === 'shift' || name === 'splice') && obj.length === 0) delete obj[0];
      return chainResult(this, obj);
    };
  });

  // 同mixin差很少添加方法
  _.each(['concat', 'join', 'slice'], function(name) {
    var method = ArrayProto[name];
    _.prototype[name] = function() {
      return chainResult(this, method.apply(this._wrapped, arguments));
    };
  });

  // _.chain的value方法
  _.prototype.value = function() {
    return this._wrapped;
  };

  //添加相應方法
  _.prototype.valueOf = _.prototype.toJSON = _.prototype.value;
  //添加相應方法
  _.prototype.toString = function() {
    return String(this._wrapped);
  };

  //對AMD的兼容
  if (typeof define == 'function' && define.amd) {
    define('underscore', [], function() {
      return _;
    });
  }
}());
相關文章
相關標籤/搜索