underscore源碼閱讀之一

這次源碼分析爲 1.8.3 version
之前曾讀過一次,但是沒有作下筆記。這次從新閱讀特製此筆記javascript

Baseline setup

underscore是包裹在一個閉包內部的防止污染全局變量java

(function(){ 
}())

若是有人不知道的話我能夠說一下
這樣匿名函數當即調用
由於函數劃分做用域
在建立做用域以後沒有引用,則不污染全局node

var root = typeof self == 'object' && self.self === self && self ||
            typeof global == 'object' && global.global === global && global ||
            this ||
            {};

顯然判斷的是運行的環境。由於多是node環境多是瀏覽器環境等。git

var previousUnderscore = root._;

若是以前存在_的話,則將其進行保存而不是簡單的替換github

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;

保存常見方法的引用。由於頻繁的查找。會損耗性能(高性能javascript)數組

var nativeIsArray = Array.isArray,
      nativeKeys = Object.keys,
      nativeCreate = Object.create;

保存es5方法引用瀏覽器

var Ctor = function(){};

Constructor的縮寫,在以後用於對象建立安全

var _ = function (obj) {
        if (obj instanceof _) return obj;
        if (!(this instanceof _)) return new _(obj);
        this._wrapped = obj;
    };

建立一個underscore的對象引用,保證不重複引用
且當用_作函數調用時候,會自動返回_對象
當_調用時候則不會新生_對象閉包

if (typeof exports != 'undefined' && !exports.nodeType) {
        if (typeof module != 'undefined' && !module.nodeType && module.exports) {
            exports = module.exports = _;
        }
        exports._ = _;
    } else {
        root._ = _;
    }

將_underscore對象掛載到合適的位置。
相似umd的兼容寫法

_.VERSION = '1.8.3';

記錄版本號

var optimizeCb = function(func, context, argCount) {
    if (context === void 0) return func;
    switch (argCount) {
      case 1: return function(value) {
        return func.call(context, value);
      };
      // The 2-parameter case has been omitted only because no current consumers
      // made use of it.
      case null:
      case 3: return function(value, index, collection) {
        return func.call(context, value, index, collection);
      };
      case 4: return function(accumulator, value, index, collection) {
        return func.call(context, accumulator, value, index, collection);
      };
    }
    return function() {
      return func.apply(context, arguments);
    };
  };

優化回調(特指函數中傳入的回調)
void 0是一個真正的undefined由於undefined是能夠被賦值的
防止undefined值被篡改
接下來就是保證回調函數的執行上下文。
若是回調函數參數只有1個,那麼咱們在迭代過程當中咱們只須要值
兩個回調函數參數的時候基本不存在這裏省略
3個的狀況相似forEach那麼傳入回調函數值(值,索引,被迭代集合對象)
4個的狀況則是累加器,相似reduce(累加器,值,索引,被迭代集合對象)
argCount不存在則直接返回一個綁定上下文的回調函數

var builtinIteratee;

一個默認的迭代器

var cb = function (value, context, argCount) {
        if (_.iteratee !== builtinIteratee) return _.iteratee(value, context);
        if (value == null) return _.identity;
        if (_.isFunction(value)) return optimizeCb(value, context, argCount);
        if (_.isObject(value)) return _.matcher(value);
        return _.property(value);
    };

爲迭代過程當中的元素生產一個回調函數, 該回調函數可以應用到集合中的每一個元素
第一個是否使用默認的迭代器
第二個 若是value爲null 則回調只是一個返回自身的函數
第三個 若是value是一個回調函數,則須要optimizeCb回爐改造回調進行優化
第四個 若是value是對象,則返回一個matcher進行對象匹配
最後 若是value只是一個字面量 則將value看作屬性名稱,默認返回一個獲取對象屬性的函數
簡單的能夠認爲cb函數是對一個大的範圍進行處理 optimizeCb 則是對一個更具體的回調函數進行優化

_.iteratee = builtinIteratee = function (value, context) {
        return cb(value, context, Infinity);
    };

underscore內置的迭代回調
Infinity是一個全局的數值,表明無窮大
這又得牽扯到javascript的安全數值了

var restArgs = function (func, startIndex) {
        startIndex = startIndex == null ? func.length - 1 : +startIndex;
        //rest判斷有沒有,沒有的話默認是參數的最後。有的話則是有的狀況。(+隱式轉換數字)
        return function () {
            var length = Math.max(arguments.length - startIndex, 0);
            //防止出現負數
            var rest = Array(length);
            //開闢數組來存儲rest
            for (var index = 0; index < length; index++) {
                rest[index] = arguments[index + startIndex];
            }
            //例如 func(a,b,...args)
            //其實是在結構一下 func(1,2,3,4,5,6,7)變成func(1,2,[3,4,5,6,7])
            switch (startIndex) {
                case 0:
                    return func.call(this, rest);
                case 1:
                    return func.call(this, arguments[0], rest);
                case 2:
                    return func.call(this, arguments[0], arguments[1], rest);
            }
            //根據rest的參數不一樣決定不一樣的回調方法
            var args = Array(startIndex + 1);
            for (index = 0; index < startIndex; index++) {
                args[index] = arguments[index];
            }
            //這是一種默認的傳參調用
            args[startIndex] = rest;
            //拼接剩餘參數。傳入函數回調
            return func.apply(this, args);
        };
    };

相似ES6的...實現
傳入參數 func 須要rest參數的數組 startIndex從哪裏開始標識rest參數, 若是不傳遞, 默認最後一個參數爲rest參數
最後是返回一個具有了args(rest)參數的函數

var createAssigner = function(keysFunc, undefinedOnly) {
    return function(obj) {
      var length = arguments.length;
      //得到參數長度
      //若是小於2或者傳入obj爲null 則直接返回
      if (length < 2 || obj == null) return obj;
      //枚舉後面的參數對象
      for (var index = 1; index < length; index++) {
      //一個迭代
      //傳入的keysFunc實際上也是一個函數
      //此時的source是對象參數
        var source = arguments[index],
            keys = keysFunc(source),
            l = keys.length;
            //遍歷對象鍵
        for (var i = 0; i < l; i++) {
          var key = keys[i];
          //若是沒有傳入undefinedOnly
          //則必定會執行鍵值對賦值。鍵值相同會直接進行覆蓋
          //若是有undefinedOnly值,則對象賦值則不會進行覆蓋
          if (!undefinedOnly || obj[key] === void 0) obj[key] = source[key];
        }
      }
      //返回處理後的obj對象
      return obj;
    };
  };
var baseCreate = function (prototype) {
        if (!_.isObject(prototype)) return {};
        if (nativeCreate) return nativeCreate(prototype);
        Ctor.prototype = prototype;
        var result = new Ctor;
        Ctor.prototype = null;
        return result;
    };

'類'的繼承。(即Object.create)而且保證掛載在原型鏈的屬性不會影響所繼承的prototype
判斷是否爲對象,不是就直接返回了。
若是有Object.create則使用Object.create
沒有的話下面至關因而一個Object.create的polyfill

var property = function(key) {
    return function(obj) {
      return obj == null ? void 0 : obj[key];
    };
  };

利用函數式編程思想
這是明顯的函數向右柯里化
先傳入key返回一個匿名函數爲obj
當調用Property(length)的時候。裏面則是爲obj == null ? void 0 : obj[length];

var deepGet = function(obj, path) {
    var length = path.length;
    for (var i = 0; i < length; i++) {
      if (obj == null) return void 0;
      obj = obj[path[i]];
    }
    return length ? obj : void 0;
  };

得到對象的深層屬性

var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;

最大安全'值'

var getLength = property('length');

像上面說的

var isArrayLike = function(collection) {
    var length = getLength(collection);
    return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
  };

判斷是否是類數組也就是簡單判斷是否是有length存在而且大於等於0且小於或等於最大安全整數


參考資料

相關文章
相關標籤/搜索