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方法
//_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方法,用于重组参数
//_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方法
baseRest
// _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;
overRest
//_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, & pebbles</p>' */ function wrap(value, wrapper) { return partial(castFunction(wrapper), value);//调用partial并将value作为第一个参数提前传入 } module.exports = wrap;
lodash--Function篇,学习完毕。