首页 > 代码库 > lodash源码学习partial,partialRight

lodash源码学习partial,partialRight

_.partial(func, [partials])

创建一个func的包装方法,调用这个方法可以提前传入部分func方法的参数.

这个方法感觉通常用在有很多参数相同的场景,然后将相同的参数提前传入、

比如

var ajax = (type,url,data,dataType,async) => {    ..//具体实现}var ajGet = _.partial(‘get‘, _,  _, ‘json‘, true)ajGet(‘/user‘,{id:1})

来看看具体实现

//partial.jsvar 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方法

//_createWrap.jsvar baseSetData = http://www.mamicode.com/require(‘./_baseSetData‘),    createBind = require(‘./_createBind‘),    createCurry = require(‘./_createCurry‘),    createHybrid = require(‘./_createHybrid‘),    createPartial = require(‘./_createPartial‘),//创建部分参数方法    getData = http://www.mamicode.com/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/73343function 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 = http://www.mamicode.com/isBindKey ? undefined : getData(func);  var newData =http://www.mamicode.com/ [    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.jsvar 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.jsvar 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方法

//composeArgs.jsvar 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从末尾开始的参数

这个方法的使用场景和partial差不多,就不分析了,直接看具体实现

//partialRight.jsvar 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.jsvar baseSetData = http://www.mamicode.com/require(‘./_baseSetData‘),    createBind = require(‘./_createBind‘),    createCurry = require(‘./_createCurry‘),    createHybrid = require(‘./_createHybrid‘),    createPartial = require(‘./_createPartial‘),//创建部分参数方法    getData = http://www.mamicode.com/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/73343function 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 = http://www.mamicode.com/isBindKey ? undefined : getData(func);  var newData =http://www.mamicode.com/ [    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.jsvar 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.jsvar 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;

 

lodash源码学习partial,partialRight