1. 程式人生 > >lodash源碼學習(10)

lodash源碼學習(10)

返回 con ... eve ole func sarg 進行 pca

_.delay(func, wait, [args])

延遲wait毫秒之後調用該函數,添加的參數為函數調用時的參數

//delay.js

var baseDelay = require(‘./_baseDelay‘),//baseDelay方法
    baseRest = require(‘./_baseRest‘),//創建使用rest參數方法
    toNumber = require(‘./toNumber‘);//轉化為數字

/**
 * 
 * @param {Function} func 需要延遲執行的方法.
 * @param {number} wait 調用等待時間.
 * @param {...*} [args] 調用參數.
 * @returns {number} 返回time id.
 * @example
 *
 * _.delay(function(text) {
 *   console.log(text);
 * }, 1000, ‘later‘);
 * // => Logs ‘later‘ after one second.
 
*/ var delay = baseRest(function(func, wait, args) { return baseDelay(func, toNumber(wait) || 0, args); }); module.exports = delay;

這個方法依賴於baseDelay方法

//_baseDelay.js

var FUNC_ERROR_TEXT = ‘Expected a function‘;

/**
 * _.delay_.defer的基本實現
 *
 * @private
 * @param {Function} func 需要延遲執行的函數.
 * @param {number} wait 調用等待時間.
 * @param {Array} args 調用參數.
 * @returns {number|Object} 返回time id.
 
*/ function baseDelay(func, wait, args) { if (typeof func != ‘function‘) { throw new TypeError(FUNC_ERROR_TEXT); } return setTimeout(function() { func.apply(undefined, args); }, wait);//調用setTimeout方法,執行func並且傳入參數 } module.exports = baseDelay;

_.defer(func, [args])

延遲1毫秒之後調用該函數,添加的參數為函數調用時的參數

//defer.js

var baseDelay = require(‘./_baseDelay‘),
    baseRest = require(‘./_baseRest‘);

/**
 *
 * @param {Function} func 需要延遲執行的函數.
 * @param {...*} [args] 調用參數.
 * @returns {number} 返回timer id.
 * @example
 *
 * _.defer(function(text) {
 *   console.log(text);
 * }, ‘deferred‘);
 * // => Logs ‘deferred‘ after one millisecond.
 */
var defer = baseRest(function(func, args) {
  return baseDelay(func, 1, args);
});

module.exports = defer;

_.flip(func)

創建一個調用func的方法,並且將參數反向.

//flip.js

var createWrap = require(‘./_createWrap‘);

var WRAP_FLIP_FLAG = 512;

/**
 *
 * @param {Function} func 需要將參數反向的方法.
 * @returns {Function} 返回處理之後的方法.
 * @example
 *
 * var flipped = _.flip(function() {
 *   return _.toArray(arguments);
 * });
 *
 * flipped(‘a‘, ‘b‘, ‘c‘, ‘d‘);
 * // => [‘d‘, ‘c‘, ‘b‘, ‘a‘]
 */
function flip(func) {
  return createWrap(func, WRAP_FLIP_FLAG);
}

module.exports = flip;

這個方法和很對方法一樣,依賴於createWrap方法和createHybrid方法,依然只分析對應部分

//_createWrap.js

var baseSetData = require(‘./_baseSetData‘),
    createBind = require(‘./_createBind‘),
    createCurry = require(‘./_createCurry‘),
    createHybrid = require(‘./_createHybrid‘),
    createPartial = require(‘./_createPartial‘),
    getData = require(‘./_getData‘),
    mergeData = require(‘./_mergeData‘),
    setData = require(‘./_setData‘),
    setWrapToString = require(‘./_setWrapToString‘),
    toInteger = require(‘./toInteger‘);

var FUNC_ERROR_TEXT = ‘Expected a function‘;

//各種方法的位掩碼標識
var WRAP_BIND_FLAG = 1,
    WRAP_BIND_KEY_FLAG = 2,
    WRAP_CURRY_FLAG = 8,
    WRAP_CURRY_RIGHT_FLAG = 16,
    WRAP_PARTIAL_FLAG = 32,
    WRAP_PARTIAL_RIGHT_FLAG = 64;

var nativeMax = Math.max;//原生最大值方法

/**
 * 創建一個函數,該函數可以創建或調用func用可選的this和部分應用的參數.
 *
 * @param {Function|string} func 需要包裝的函數.
 * @param {number} bitmask 位掩碼標識
 * @param {*} [thisArg] func的this對象
 * @param {Array} [partials] 應用的參數
 * @param {Array} [holders] 占位符的索引
 * @param {Array} [argPos] .
 * @param {number} [ary] .
 * @param {number} [arity] 可用參數數量.
 * @returns {Function} 返回包裝之後的函數.
 */
 //lodash使用BitMask來進行各種方法的表示,BitMask使用方法可以看 http://geek.csdn.net/news/detail/73343

function createWrap(func, bitmask, thisArg, partials, holders, argPos, ary, arity) {
  var isBindKey = bitmask & WRAP_BIND_KEY_FLAG;//是否是bindKey方法(不是)
  if (!isBindKey && typeof func != ‘function‘) {
    throw new TypeError(FUNC_ERROR_TEXT);
  }
  var length = partials ? partials.length : 0;//應用的參數個數,為0
  if (!length) {
    bitmask &= ~(WRAP_PARTIAL_FLAG | WRAP_PARTIAL_RIGHT_FLAG);//清除WRAP_PARTIAL_FLAG和WRAP_PARTIAL_RIGHT_FLAG
    partials = holders = undefined;//部分參數和占位符索引都為undefined
  }
  ary = ary === undefined ? ary : nativeMax(toInteger(ary), 0);
  arity = arity === undefined ? arity : toInteger(arity);
  length -= holders ? holders.length : 0;

  if (bitmask & WRAP_PARTIAL_RIGHT_FLAG) {//是否是WRAP_PARTIAL_RIGHT_FLAG(不是,跳過)
    var partialsRight = partials,
        holdersRight = holders;

    partials = holders = undefined;
  }
  var data = isBindKey ? undefined : getData(func);//得到func的元數據

  var newData = [
    func, bitmask, thisArg, partials, holders, partialsRight, holdersRight,
    argPos, ary, arity
  ];//將所有參數賦值給newData

  if (data) {
    mergeData(newData, data);//合並data
  }
  func = newData[0];
  bitmask = newData[1];
  thisArg = newData[2];
  partials = newData[3];
  holders = newData[4];
  arity = newData[9] = newData[9] === undefined
    ? (isBindKey ? 0 : func.length)
    : nativeMax(newData[9] - length, 0);

  if (!arity && bitmask & (WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG)) {
    bitmask &= ~(WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG);
  }
  if (!bitmask || bitmask == WRAP_BIND_FLAG) {
    var result = createBind(func, bitmask, thisArg);
  } else if (bitmask == WRAP_CURRY_FLAG || bitmask == WRAP_CURRY_RIGHT_FLAG) {
    result = createCurry(func, bitmask, arity);
  } else if ((bitmask == WRAP_PARTIAL_FLAG || bitmask == (WRAP_BIND_FLAG | WRAP_PARTIAL_FLAG)) && !holders.length) {
    result = createPartial(func, bitmask, thisArg, partials);
  } else {
    result = createHybrid.apply(undefined, newData);//直接調用createHybrid方法
  }
  var setter = data ? baseSetData : setData;//設置data的元數據(暫不分析)
  return setWrapToString(setter(result, newData), func, bitmask);//給包裝之後的方法添加元數據(用於優化),添加toStirng方法,並返回func(具體實現暫不分析)
}

module.exports = createWrap;

createHybrid方法

//_createHybrid.js

var composeArgs = require(‘./_composeArgs‘),
    composeArgsRight = require(‘./_composeArgsRight‘),
    countHolders = require(‘./_countHolders‘),
    createCtor = require(‘./_createCtor‘),
    createRecurry = require(‘./_createRecurry‘),
    getHolder = require(‘./_getHolder‘),
    reorder = require(‘./_reorder‘),
    replaceHolders = require(‘./_replaceHolders‘),
    root = require(‘./_root‘);

//位掩碼標識
var WRAP_BIND_FLAG = 1,
    WRAP_BIND_KEY_FLAG = 2,
    WRAP_CURRY_FLAG = 8,
    WRAP_CURRY_RIGHT_FLAG = 16,
    WRAP_ARY_FLAG = 128,
    WRAP_FLIP_FLAG = 512;

/**
 * 創建一個包裝函數,調用func使用可選的thisArg,應用部分參數和柯裏化.
 *
 * @param {Function|string} func 需要包裝的方法.
 * @param {number} bitmask 位掩碼標識
 * @param {*} [thisArg] .
 * @param {Array} [partials] 實現傳入的參數.
 * @param {Array} [holders] 占位符.
 * @param {Array} [partialsRight] .
 * @param {Array} [holdersRight] .
 * @param {Array} [argPos] .
 * @param {number} [ary] .
 * @param {number} [arity] 可用函數參數數量.
 * @returns {Function} 返回新的包裝函數.
 */
function createHybrid(func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity) {
  var isAry = bitmask & WRAP_ARY_FLAG,
      isBind = bitmask & WRAP_BIND_FLAG,
      isBindKey = bitmask & WRAP_BIND_KEY_FLAG,
      isCurried = bitmask & (WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG),
      isFlip = bitmask & WRAP_FLIP_FLAG,//是flip方法
      Ctor = isBindKey ? undefined : createCtor(func);

  function wrapper() {
    var length = arguments.length,//參數個數
        args = Array(length),//保存所有參數
        index = length;//參數數組索引

    while (index--) {//遍歷參數,將所有參數存入args
      args[index] = arguments[index];
    }
    if (isCurried) {
      var placeholder = getHolder(wrapper),
          holdersCount = countHolders(args, placeholder);
    }
    if (partials) {
      args = composeArgs(args, partials, holders, isCurried);
    }
    if (partialsRight) {
      args = composeArgsRight(args, partialsRight, holdersRight, isCurried);
    }
    length -= holdersCount;
    if (isCurried && length < arity) {
      var newHolders = replaceHolders(args, placeholder);
      return createRecurry(
        func, bitmask, createHybrid, wrapper.placeholder, thisArg,
        args, newHolders, argPos, ary, arity - length
      );
    }
    var thisBinding = isBind ? thisArg : this,
        fn = isBindKey ? thisBinding[func] : func;

    length = args.length;//參數長度
    if (argPos) {
      args = reorder(args, argPos);
    } else if (isFlip && length > 1) {
      args.reverse();//直接將args反向,就這麽簡單。。。。
    }
    if (isAry && ary < length) {
      args.length = ary;
    }
    if (this && this !== root && this instanceof wrapper) {
      fn = Ctor || createCtor(fn);
    }
    return fn.apply(thisBinding, args);//調用fn,並且傳入args
  }
  return wrapper;//返回包裝方法
}
module.exports = createHybrid;

_.memoize(func, [resolver])

創建一個可以記錄func返回結果的方法,默認緩存key的值為func的第一個參數.如果傳入了resolver,key為resolver的執行結果,resolver的參數為傳入func的參數
* 如果需要修改記錄結果,可以使用memoized.cache.set(key,value)

//memorize.js

var MapCache = require(‘./_MapCache‘);//map緩存對象

/** Error message constants. */
var FUNC_ERROR_TEXT = ‘Expected a function‘;

/**
 *  
 *
 * @param {Function} func 需要記錄結果的方法.
 * @param {Function} [resolver] 緩存key的生成方法.
 * @returns {Function} 生成記憶方法.
 * @example
 *
 * var object = { ‘a‘: 1, ‘b‘: 2 };
 * var other = { ‘c‘: 3, ‘d‘: 4 };
 *
 * var values = _.memoize(_.values);
 * values(object);
 * // => [1, 2]
 *
 * values(other);
 * // => [3, 4]
 *
 * object.a = 2;
 * values(object);
 * // => [1, 2]
 *
 * // Modify the result cache.
 * values.cache.set(object, [‘a‘, ‘b‘]);
 * values(object);
 * // => [‘a‘, ‘b‘]
 *
 * // Replace `_.memoize.Cache`.
 * _.memoize.Cache = WeakMap;
 */
function memoize(func, resolver) {
  if (typeof func != ‘function‘ || (resolver != null && typeof resolver != ‘function‘)) {
    throw new TypeError(FUNC_ERROR_TEXT);
  }
  var memoized = function() {
    var args = arguments,//參數對象
        key = resolver ? resolver.apply(this, args) : args[0],//緩存key值
        cache = memoized.cache;//記憶函數的緩存對象

    if (cache.has(key)) {//如果緩存中含有key
      return cache.get(key);//返回對應的value
    }
    var result = func.apply(this, args);//沒有值,調用func得到結果
    memoized.cache = cache.set(key, result) || cache;//將結果存入緩存中
    return result;//返回結果
  };
  memoized.cache = new (memoize.Cache || MapCache);//初始化記憶方法的緩存。
  return memoized;//返回記憶方法
}

// 暴露MapCache
memoize.Cache = MapCache;

module.exports = memoize;

_.negate(predicate)

創建一個方法,得到判斷方法的結果的否定,判斷方法的this為生成的否定方法的this

//negate.js


var FUNC_ERROR_TEXT = ‘Expected a function‘;

/**
 *
 * @param {Function} predicate 需要否定的判斷方法.
 * @returns {Function} 返回新的否定方法.
 * @example
 *
 * function isEven(n) {
 *   return n % 2 == 0;
 * }
 *
 * _.filter([1, 2, 3, 4, 5, 6], _.negate(isEven));
 * // => [1, 3, 5]
 */
function negate(predicate) {
  if (typeof predicate != ‘function‘) {
    throw new TypeError(FUNC_ERROR_TEXT);
  }
  return function() {
    var args = arguments;//參數對象
    switch (args.length) {//參數小於3個,通過call調用判斷方法,並返回否定的結果
      case 0: return !predicate.call(this);
      case 1: return !predicate.call(this, args[0]);
      case 2: return !predicate.call(this, args[0], args[1]);
      case 3: return !predicate.call(this, args[0], args[1], args[2]);
    }
    return !predicate.apply(this, args);//參數大於3個,通過apply調用判斷方法,並返回否定的結果
  };
}

module.exports = negate;

_.once(func)

創建一個方法,限制func只能調用一次

//once.js

var before = require(‘./before‘);//before方法,見源碼學習(7)

/**
 * 
 *
 * @param {Function} func 需要限制調用的方法.
 * @returns {Function} 返回限制方法.
 * @example
 *
 * var initialize = _.once(createApplication);
 * initialize();
 * initialize();
 * // => `createApplication` is invoked once
 */
function once(func) {
  return before(2, func);//調用before方法,調用次數小於2
}

module.exports = once;

_.overArgs(func, [transforms=[_.identity]])

//overArgs.js


var apply = require(‘./_apply‘),//同Function.apply
    arrayMap = require(‘./_arrayMap‘),//同Array.map
    baseFlatten = require(‘./_baseFlatten‘),//數組扁平化方法,見源碼學習(1)
    baseIteratee = require(‘./_baseIteratee‘),//遍歷器封裝
    baseRest = require(‘./_baseRest‘),//創建具有rest參數的方法
    baseUnary = require(‘./_baseUnary‘),//創建只有一個參數的方法
    castRest = require(‘./_castRest‘),//同baseRest
    isArray = require(‘./isArray‘);//同Array.isArray

var nativeMin = Math.min;//原生求最小值方法

/**
 * 創建一個方法,調用func並且將調用的參數進行對應的轉化.
 *
 * @param {Function} func 需要保裝的方法.
 * @param {...(Function|Function[])} [transforms=[_.identity]] 參數對應的轉換方法.
 * @returns {Function} 返回新的方法.
 * @example
 *
 * function doubled(n) {
 *   return n * 2;
 * }
 *
 * function square(n) {
 *   return n * n;
 * }
 *
 * var func = _.overArgs(function(x, y) {
 *   return [x, y];
 * }, [square, doubled]);
 *
 * func(9, 3);
 * // => [81, 6]
 *
 * func(10, 5);
 * // => [100, 10]
 */
var overArgs = castRest(function(func, transforms) {

  //如果轉換器只有一個且為數組,遍歷這個數組,並且進行封裝
  //否則將傳入的轉換器扁平化,遍歷數組,並且進行封裝封裝
  transforms = (transforms.length == 1 && isArray(transforms[0])) 
    ? arrayMap(transforms[0], baseUnary(baseIteratee))
    : arrayMap(baseFlatten(transforms, 1), baseUnary(baseIteratee));

  var funcsLength = transforms.length;//轉換器的長度
  return baseRest(function(args) {
    var index = -1,//參數索引
        length = nativeMin(args.length, funcsLength);//需要轉化的個數

    while (++index < length) {//循環,轉化對應的參數
      args[index] = transforms[index].call(this, args[index]);
    }
    return apply(func, this, args);//調用func
  });
});

module.exports = overArgs;

lodash源碼學習(10)