lodash源碼學習(12)

_.rearg(func, indexes)

建立一個func的包裝方法,能夠指定傳入參數做爲func的參數的索引。好比指定索引[2,0,1],則傳入的第一個參數做爲func的第三個參數被調用,第二個參數做爲func的第一個參數。。以此類推.
//rearg.js

var createWrap = require('./_createWrap'),//函數包裝方法
    flatRest = require('./_flatRest');//扁平化rest參數,即rearg(fn,[2,0,1]) = rearg(fn, 2, 0, 1)

var WRAP_REARG_FLAG = 256; //rearg的位掩碼標識

/**
 *
 * @param {Function} func The 須要包裝的方法.
 * @param {...(number|number[])} indexes 指定參數的索引值.
 * @returns {Function} 返回新的方法.
 * @example
 *
 * var rearged = _.rearg(function(a, b, c) {
 *   return [a, b, c];
 * }, [2, 0, 1]);
 *
 * rearged('b', 'c', 'a')
 * // => ['a', 'b', 'c']
 */
 var rearg = flatRest(function(func, indexes) { //支持使用數組或者rest參數形式
  return createWrap(func, WRAP_REARG_FLAG, undefined, undefined, undefined, indexes);//調用包裝方法
});

module.exports = rearg;

依然是依賴於createWrap和createHybrid方法html

//_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;
if (!isBindKey && typeof func != 'function') {
throw new TypeError(FUNC_ERROR_TEXT);
}
var length = partials ? partials.length : 0;//傳入的參數個數,不傳爲0
if (!length) {//若是沒有傳入partials
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);

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

if (data) {
mergeData(newData, 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] this對象.
 * @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,
      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); //調用reorder方法 
    } else if (isFlip && length > 1) {
      args.reverse();
    }
    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;

能夠看到這裏依賴於reorder方法,用於重組參數es6

//_reorder.js

var copyArray = require('./_copyArray'),//拷貝數組
    isIndex = require('./_isIndex');//是否爲索引


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

/**
 * 對數組進行根據指定的索引從新排序.
 *
 * @private
 * @param {Array} array 須要排序的數組.
 * @param {Array} indexes 排序的索引.
 * @returns {Array} 返回這個數組.
 */
function reorder(array, indexes) {
  var arrLength = array.length,
      length = nativeMin(indexes.length, arrLength),//數組長度
      oldArray = copyArray(array);

  while (length--) {//對索引值進行遍歷
    var index = indexes[length];//索引值
    array[length] = isIndex(index, arrLength) ? oldArray[index] : undefined; //對數組依次賦值對應索引值的元素
  }
  return array;//返回這個數組
}

module.exports = reorder;

_.rest(func, [start=func.length-1])

建立一個方法,支持rest參數形式.
這個方法實現了和es6的rest參數同樣的效果,形如fn(...args),rest參數簡化了使用arguments獲取多餘參數的方法。lodash中大量的方法都依賴於rest參數,好比上一次學習的partial,partialRight等等。
//rest.js

var baseRest = require('./_baseRest'),//包裝函數使支持rest參數
    toInteger = require('./toInteger'); //轉換爲整型


var FUNC_ERROR_TEXT = 'Expected a function';

/**
 *
 *
 * @param {Function} func 須要包裝的方法.
 * @param {number} [start=func.length-1] rest參數的開始位置.
 * @returns {Function} 返回一個新的方法.
 * @example
 *
 * var say = _.rest(function(what, names) {
 *   return what + ' ' + _.initial(names).join(', ') +
 *     (_.size(names) > 1 ? ', & ' : '') + _.last(names);
 * });
 *
 * say('hello', 'fred', 'barney', 'pebbles');
 * // => 'hello fred, barney, & pebbles'
 */
function rest(func, start) {
  if (typeof func != 'function') {
    throw new TypeError(FUNC_ERROR_TEXT);
  }
  start = start === undefined ? start : toInteger(start); //若是傳入了start,將其轉換爲整型
  return baseRest(func, start);
}

module.exports = rest;

依賴於baseRest方法和overRest方法數組

baseRestapp

// _baseRest.js

var identity = require('./identity'), //返回傳入的第一個值
    overRest = require('./_overRest'),
    setToString = require('./_setToString'); //設置元數據和toString方法

/**
 * _rest方法的基本實現.
 *
 * @private
 * @param {Function} func 須要使用rest參數的方法.
 * @param {number} [start=func.length-1] rest參數的開始位置.
 * @returns {Function} 返回新的方法.
 */
function baseRest(func, start) {
  return setToString(overRest(func, start, identity), func + '');
}

module.exports = baseRest;

overRestide

//_overRest.js

var apply = require('./_apply');//同function.apply

var nativeMax = Math.max;

/**
 * rest參數轉換方法.
 *
 * @private
 * @param {Function} func 須要包裝的方法.
 * @param {number} [start=func.length-1] 指定rest參數的起始位置.
 * @param {Function} transform rest數組轉換器.
 * @returns {Function} 返回新的函數.
 */
function overRest(func, start, transform) {
  start = nativeMax(start === undefined ? (func.length - 1) : start, 0); //限制start在0和總參數長度之間
  return function() {
    var args = arguments,
        index = -1,
        length = nativeMax(args.length - start, 0),
        array = Array(length);

    while (++index < length) { //遍歷參數,將start以後的參數保存到array中
      array[index] = args[start + index];
    }
    index = -1;
    var otherArgs = Array(start + 1); //調用func的參數
    while (++index < start) {
      otherArgs[index] = args[index]; // start以前的參數爲相應傳入的參數
    }
    //start位置的參數爲array
    otherArgs[start] = transform(array);
    //調用fn,傳入otherArgs
    //即若是start爲1 ,fn(1,2,3,4) ==> fn(1,[2,3,4])
    return apply(func, this, otherArgs); 
  };
}

module.exports = overRest;

_.spread(func, [start=0])

建立一個方法調用func,傳入一個數組做爲func的參數,相似apply.
//_spread.js

var apply = require('./_apply'), //apply方法
    arrayPush = require('./_arrayPush'), //push方法
    baseRest = require('./_baseRest'), //包裝函數使支持rest參數
    castSlice = require('./_castSlice'), //slice方法
    toInteger = require('./toInteger'); //轉換爲整型

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

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

/**
 *
 * @static
 * @memberOf _
 * @since 3.2.0
 * @category Function
 * @param {Function} func 須要包裝的方法.
 * @param {number} [start=0] 須要展開的開始位置.
 * @returns {Function} 返回一個新的函數.
 * @example
 *
 * var say = _.spread(function(who, what) {
 *   return who + ' says ' + what;
 * });
 *
 * say(['fred', 'hello']);
 * // => 'fred says hello'
 *
 * var numbers = Promise.all([
 *   Promise.resolve(40),
 *   Promise.resolve(36)
 * ]);
 *
 * numbers.then(_.spread(function(x, y) {
 *   return x + y;
 * }));
 * // => a Promise of 76
 */
function spread(func, start) {
  if (typeof func != 'function') {
    throw new TypeError(FUNC_ERROR_TEXT);
  }
  start = start == null ? 0 : nativeMax(toInteger(start), 0); //確保start大於0
  return baseRest(function(args) {
    var array = args[start],
        otherArgs = castSlice(args, 0, start);//將要調用的參數,start以前的參數不變

    if (array) {
      arrayPush(otherArgs, array); //若是有start位置的值,傳入otherArgs
    }
    return apply(func, this, otherArgs); //調用func,而且將otherArgs做爲參數傳入
  });
}

module.exports = spread;

_.unary(func)

建立一個方法只接受一個參數,忽略其餘全部參數.
//unary.js

var ary = require('./ary'); //建立一個方法調用func用n個參數,忽略其餘全部參數.(見源碼學習7)

/**
 * 
 *
 * @param {Function} func 須要處理的函數。
 * @returns {Function} 返回處理後的方法.
 * @example
 *
 * _.map(['6', '8', '10'], _.unary(parseInt));
 * // => [6, 8, 10]
 */
function unary(func) {
  return ary(func, 1);//調用ary方法,只接受一個參數。
}

module.exports = unary;

_.wrap(value, [wrapper=identity])

建立一個方法接受value和將要調用的方法wapper,而且將value做爲wrapper的第一個參數.
//wrap.js

var castFunction = require('./_castFunction'), //確保爲函數
    partial = require('./partial');//partial方法(見lodash源碼學習partial,partialRight)

/**
 * 
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Function
 * @param {*} value The value to wrap.
 * @param {Function} [wrapper=identity] The wrapper function.
 * @returns {Function} Returns the new function.
 * @example
 *
 * var p = _.wrap(_.escape, function(func, text) {
 *   return '<p>' + func(text) + '</p>';
 * });
 *
 * p('fred, barney, & pebbles');
 * // => '<p>fred, barney, &amp; pebbles</p>'
 */
function wrap(value, wrapper) {
  return partial(castFunction(wrapper), value);//調用partial並將value做爲第一個參數提早傳入
}

module.exports = wrap;

lodash--Function篇,學習完畢。函數

相關文章
相關標籤/搜索