lodash源碼學習partial,partialRight
阿新 • • 發佈:2017-08-12
red nts max arguments all rep 多參數 使用 text
_.partial(func, [partials])
創建一個func的包裝方法,調用這個方法可以提前傳入部分func方法的參數.
這個方法感覺通常用在有很多參數相同的場景,然後將相同的參數提前傳入、
比如
var ajax = (type,url,data,dataType,async) => { ..//具體實現 } var ajGet = _.partial(‘get‘, _, _, ‘json‘, true) ajGet(‘/user‘,{id:1})
來看看具體實現
//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方法
//_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方法
//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從末尾開始的參數
這個方法的使用場景和partial差不多,就不分析了,直接看具體實現
//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;
lodash源碼學習partial,partialRight