lodash源碼學習partial,partialRight

_.partial(func, [partials])

建立一個func的包裝方法,調用這個方法能夠提早傳入部分func方法的參數.ajax

這個方法感受一般用在有不少參數相同的場景,而後將相同的參數提早傳入、json

好比數組

var ajax = (type,url,data,dataType,async) => {
    ..//具體實現
}
var ajGet = _.partial('get', _,  _, 'json', true)

ajGet('/user',{id:1})

來看看具體實現app

//partial.js

var baseRest = require('./_baseRest'),//生成具備rest參數的方法
    createWrap = require('./_createWrap'),//函數包裝方法
    getHolder = require('./_getHolder'),//獲得佔位符標識
    replaceHolders = require('./_replaceHolders');//替換佔位符標識,並返回佔位符索引的數組

var WRAP_PARTIAL_FLAG = 32;//partial標識

/**
 *    
 *
 *
 * @param {Function} func 須要包裝的方法.
 * @param {...*} [partials] 提早傳入的參數.
 * @returns {Function} 返回包裝以後的方法.
 * @example
 *
 * function greet(greeting, name) {
 *   return greeting + ' ' + name;
 * }
 *
 * var sayHelloTo = _.partial(greet, 'hello');
 * sayHelloTo('fred');
 * // => 'hello fred'
 *
 * // Partially applied with placeholders.
 * var greetFred = _.partial(greet, _, 'fred');
 * greetFred('hi');
 * // => 'hi fred'
 */
var partial = baseRest(function(func, partials) {//處理partials,支持rest寫法
  var holders = replaceHolders(partials, getHolder(partial));//獲得佔位符索引
  return createWrap(func, WRAP_PARTIAL_FLAG, undefined, partials, holders);//調用createWrap方法
});

// Assign default placeholders.
partial.placeholder = {};

module.exports = partial;

這個方法一樣依賴於createWrap方法async

//_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);//若是沒有佔位符,調用createPartial方法
  } 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;

依賴的createPartial方法函數

//_createPartial.js

var apply = require('./_apply'),//同Function.apply
    createCtor = require('./_createCtor'),//建立一個能夠建立函數實例的方法
    root = require('./_root');//根元素

var WRAP_BIND_FLAG = 1;//bind方法位掩碼

/**
 * 建立一個func的包裝方法,調用這個方法可使用可選的this對象和提早傳入部分參數.
 *
 * @private
 * @param {Function} func 須要包裝的方法.
 * @param {number} 位掩碼標識.
 * @param {*} thisArg func的this對象.
 * @param {Array} partials 提早傳入的參數.
 * @returns {Function} 返回新的包裝方法.
 */
function createPartial(func, bitmask, thisArg, partials) {
  var isBind = bitmask & WRAP_BIND_FLAG,//是不是bind方法
      Ctor = createCtor(func);//用於建立實例的構造器

  function wrapper() {
    var argsIndex = -1,//參數索引
        argsLength = arguments.length,//傳入參數個數
        leftIndex = -1,//提早傳入參數索引
        leftLength = partials.length,//提早傳入參數個數
        args = Array(leftLength + argsLength),//總參數
        fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;//若是是根元素調用,使用func,不然使用Ctor

    while (++leftIndex < leftLength) {//遍歷partials,將參數做爲調用方法的前面的參數
      args[leftIndex] = partials[leftIndex];
    }
    while (argsLength--) {//遍歷傳入的參數,將其做爲調用方法的後面的參數
      args[leftIndex++] = arguments[++argsIndex];
    }
    return apply(fn, isBind ? thisArg : this, args);//調用fn,傳入this對象和參數
  }
  return wrapper;//返回wrapper方法
}

module.exports = createPartial;

依賴的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) {//執行composeArgs,對參數進行組合
      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();
    }
    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;

依賴的composeArgs方法ui

//composeArgs.js

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

/**
 * 建立一個由提早傳入的參數,佔位符和傳入的參數組成的一維數組.
 *
 * @private
 * @param {Array} args 提供的參數.
 * @param {Array} partials 提早傳入的參數.
 * @param {Array} holders 提早傳入參數中的佔位符索引.
 * @params {boolean} [isCurried] 指定是否組成一個柯里化函數.
 * @returns {Array} 返回新的參數集合.
 */
function composeArgs(args, partials, holders, isCurried) {
  var argsIndex = -1,//傳入參數索引
      argsLength = args.length,//參數個數
      holdersLength = holders.length,//佔位符個數
      leftIndex = -1,//提早傳入參數索引
      leftLength = partials.length,//提早傳入參數個數
      rangeLength = nativeMax(argsLength - holdersLength, 0),//實際參數個數
      result = Array(leftLength + rangeLength),//返回結果數組
      isUncurried = !isCurried;//是否並未柯里化

  while (++leftIndex < leftLength) {//遍歷partials,將其傳入result
    result[leftIndex] = partials[leftIndex];
  }
  while (++argsIndex < holdersLength) {//遍歷holders,將result中對應索引的值,換成傳入的參數對應的值
    if (isUncurried || argsIndex < argsLength) {
      result[holders[argsIndex]] = args[argsIndex];
    }
  }
  while (rangeLength--) {//遍歷args,將剩餘的參數傳入reslut
    result[leftIndex++] = args[argsIndex++];
  }
  return result;//返回生產的完整參數數組
}

module.exports = composeArgs;

_.partialRight(func, [partials])

和_.partial很像,只不過提早傳入的參數是func從末尾開始的參數this

這個方法的使用場景和partial差很少,就不分析了,直接看具體實現url

//partialRight.js

var baseRest = require('./_baseRest'),//生成具備rest參數的方法
    createWrap = require('./_createWrap'),//函數包裝方法
    getHolder = require('./_getHolder'),//獲得佔位符標識
    replaceHolders = require('./_replaceHolders');//替換佔位符標識,並返回佔位符索引的數組

var WRAP_PARTIAL_RIGHT_FLAG = 64;//partialRight位掩碼標識

/**
 * 
 *
 * @param {Function} func 須要包裝的方法.
 * @param {...*} [partials] 提早傳入的參數.
 * @returns {Function} 返回包裝以後的方法.
 * @example
 *
 * function greet(greeting, name) {
 *   return greeting + ' ' + name;
 * }
 *
 * var greetFred = _.partialRight(greet, 'fred');
 * greetFred('hi');
 * // => 'hi fred'
 *
 * // Partially applied with placeholders.
 * var sayHelloTo = _.partialRight(greet, 'hello', _);
 * sayHelloTo('fred');
 * // => 'hello fred'
 */
var partialRight = baseRest(function(func, partials) {//處理partials,支持rest寫法
  var holders = replaceHolders(partials, getHolder(partialRight));//獲得佔位符索引
  return createWrap(func, WRAP_PARTIAL_RIGHT_FLAG, undefined, partials, holders);//調用createWrap方法
});

// Assign default placeholders.
partialRight.placeholder = {};

module.exports = partialRight;

基本上實現都和partial同樣

//_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) {//執行composeArgsRight,對參數進行組合
      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();
    }
    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;

composeArgsRight

//_composeArgsRight.js

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

/**
 * 和composeArgs很像,只是組合參數的方向相反
 *
* @private
 * @param {Array} args 提供的參數.
 * @param {Array} partials 提早傳入的參數.
 * @param {Array} holders 提早傳入參數中的佔位符索引.
 * @params {boolean} [isCurried] 指定是否組成一個柯里化函數.
 * @returns {Array} 返回新的參數集合.
 */
function composeArgsRight(args, partials, holders, isCurried) {
  var argsIndex = -1,//參數索引
      argsLength = args.length,//傳入參數個數
      holdersIndex = -1,//佔位符索引
      holdersLength = holders.length,//佔位符個數
      rightIndex = -1,//提早傳入參數索引
      rightLength = partials.length,//提早傳入參數個數
      rangeLength = nativeMax(argsLength - holdersLength, 0),//實際傳入參數個數
      result = Array(rangeLength + rightLength),//結果參數數組
      isUncurried = !isCurried;

  while (++argsIndex < rangeLength) {//遍歷傳入參數,將其傳入result
    result[argsIndex] = args[argsIndex];
  }
  var offset = argsIndex;
  while (++rightIndex < rightLength) {//遍歷partials,將其傳入result
    result[offset + rightIndex] = partials[rightIndex];
  }
  while (++holdersIndex < holdersLength) {//遍歷佔位符索引,將result中對應索引的值,換成傳入的參數對應的值
    if (isUncurried || argsIndex < argsLength) {
      result[offset + holders[holdersIndex]] = args[argsIndex++];
    }
  }
  return result;//返回生成的完整參數數組
}

module.exports = composeArgsRight;
相關文章
相關標籤/搜索