繼續學習lodash,下面是Date篇,Date篇只有一個方法node
「Date」 Methods
_.now()
獲得1970 年 1 月 1日午夜與當前日期和時間之間的毫秒數。數組
//now.js var root = require('./_root');//運行環境,node環境下爲global,瀏覽器環境爲window /** * * * @returns {number} 獲得一個時間戳. * @example * * _.defer(function(stamp) { * console.log(_.now() - stamp); * }, _.now()); * // => Logs the number of milliseconds it took for the deferred invocation. */ var now = function() { return root.Date.now();//直接調用Date對象的now方法 }; module.exports = now;
接下來是函數篇,我的感受是lodash最複雜的部分。源碼讀起來也比較艱難。。瀏覽器
_.after(n, func)
建立一個方法當它被執行n次以後調用func方法。閉包
//after.js var toInteger = require('./toInteger');//轉化爲整型 /** Error message constants. */ var FUNC_ERROR_TEXT = 'Expected a function'; /** * * * @param {number} n 在調用func以前須要執行的次數. * @param {Function} func 受限制的方法. * @returns {Function} 返回新的受限制的方法. * @example * * var saves = ['profile', 'settings']; * * var done = _.after(saves.length, function() { * console.log('done saving!'); * }); * * _.forEach(saves, function(type) { * asyncSave({ 'type': type, 'complete': done }); * }); * // => Logs 'done saving!' after the two async saves have completed. */ function after(n, func) { if (typeof func != 'function') {//若是不是函數,拋出TypeError throw new TypeError(FUNC_ERROR_TEXT); } n = toInteger(n);//閉包中保存的n值 return function() { if (--n < 1) {//每次執行n-1,若是比1小,調用func方法 return func.apply(this, arguments); } }; } module.exports = after;
_.ary(func, [n=func.length])
建立一個方法調用func用n個參數,忽略其餘全部參數.app
//ary.js var createWrap = require('./_createWrap');//包裝函數 var WRAP_ARY_FLAG = 128; //ary的位掩嘛 /** * * * @static * @memberOf _ * @since 3.0.0 * @category Function * @param {Function} func 須要包裝的函數. * @param {number} [n=func.length] 參數個數. * @param- {Object} [guard] 是否能做爲遍歷參數被_.map這樣的方法調用. * @returns {Function} 返回新的包裝以後的函數. * @example * * _.map(['6', '8', '10'], _.ary(parseInt, 1)); * // => [6, 8, 10] */ function ary(func, n, guard) { n = guard ? undefined : n; n = (func && n == null) ? func.length : n;//若是沒傳n,默認參數數量func的參數個數 return createWrap(func, WRAP_ARY_FLAG, undefined, undefined, undefined, undefined, n);//調用createWrap方法,並將結果返回 } module.exports = ary;
這個方法包括後面的不少方法都依賴於createWrap方法,這個方法由於是不少方法的基礎,全部比較複雜,咱們先分析跟ary方法相關的部分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] . * @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;//是不是bindKey方法(不是,跳過) if (!isBindKey && typeof func != 'function') { throw new TypeError(FUNC_ERROR_TEXT); } var length = partials ? partials.length : 0;//length=0 if (!length) { bitmask &= ~(WRAP_PARTIAL_FLAG | WRAP_PARTIAL_RIGHT_FLAG);//清除WRAP_PARTIAL_FLAG和WRAP_PARTIAL_RIGHT_FLAG partials = holders = undefined; } ary = ary === undefined ? ary : nativeMax(toInteger(ary), 0); arity = arity === undefined ? arity : toInteger(arity);//處理一下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);//獲得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);//再次處理一下arity 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方法,這個方法也是lodash函數篇的核心方法之一,一樣先分析跟ary相關的部分函數
//_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] . * @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,//是不是ary(是的) 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); } else if (isFlip && length > 1) { args.reverse(); } if (isAry && ary < length) {//進入這裏,將參數的長度縮減到ary的數量 args.length = ary; } if (this && this !== root && this instanceof wrapper) { fn = Ctor || createCtor(fn); } return fn.apply(thisBinding, args);//調用func,而且傳入的參數個數爲ary個 } return wrapper; } module.exports = createHybrid;
至此ary方法也算是分析完畢,自己並不複雜,就是將傳入的參數減小到指定的個數而已。學習
_.before(n, func)
建立一個調用func的函數,該函數具備建立函數的綁定和參數,它能被調用次數少於n次。對建立函數的後續調用將返回最後一個func調用的結果。優化
//before.js var toInteger = require('./toInteger');//轉化爲整型 var FUNC_ERROR_TEXT = 'Expected a function'; /** * * * @param {number} n func再也不被調用的執行次數上限. * @param {Function} func 須要限制的方法. * @returns {Function} Returns the new restricted function. * @example * * jQuery(element).on('click', _.before(5, addContactToList)); * // => Allows adding up to 4 contacts to the list. */ function before(n, func) { var result; if (typeof func != 'function') {//不是方法,拋出錯誤 throw new TypeError(FUNC_ERROR_TEXT); } n = toInteger(n);//還能夠執行的n值 return function() { if (--n > 0) {//若是n-1比0大,也就是n>=1,調用func,並將結果賦值給result result = func.apply(this, arguments); } if (n <= 1) {//銷燬func func = undefined; } return result;//返回result }; } module.exports = before;
_.bind(func, thisArg, [partials])
建立一個func的包裝方法,調用這個方法能夠使用可選的this對象和提早傳入部分參數.ui
//bind.js var baseRest = require('./_baseRest'),//生產具備rest參數的方法 createWrap = require('./_createWrap'),//函數包裝方法 getHolder = require('./_getHolder'),//獲得佔位符標識 replaceHolders = require('./_replaceHolders');//替換佔位符 var WRAP_BIND_FLAG = 1,//bind位掩碼 WRAP_PARTIAL_FLAG = 32;//partial位掩碼 /** * * * _.bind.placeholder的值默認爲`_` * * * @param {Function} func 須要綁定的函數. * @param {*} thisArg The func的this對象. * @param {...*} [partials] 提早傳入的參數. * @returns {Function} 返回新的已綁定的方法. * @example * * function greet(greeting, punctuation) { * return greeting + ' ' + this.user + punctuation; * } * * var object = { 'user': 'fred' }; * * var bound = _.bind(greet, object, 'hi'); * bound('!'); * // => 'hi fred!' * * // Bound with placeholders. * var bound = _.bind(greet, object, _, '!'); * bound('hi'); * // => 'hi fred!' */ var bind = baseRest(function(func, thisArg, partials) { var bitmask = WRAP_BIND_FLAG;//位掩碼爲bind if (partials.length) {//若是有傳入參數 var holders = replaceHolders(partials, getHolder(bind));//獲得佔位符的索引 bitmask |= WRAP_PARTIAL_FLAG;//位掩碼加上partial的標識 } return createWrap(func, bitmask, thisArg, partials, holders);//調用createWrap方法,返回包裝以後的方法 }); // 對默認的placeholder賦值. bind.placeholder = {}; module.exports = bind;
一樣是依賴於createWrap方法,依然只分析對應部分
//_createWrap.js var baseSetData = require('./_baseSetData'), createBind = require('./_createBind'),//建立bind方法 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;//是不是bindKey方法(不是,跳過) if (!isBindKey && typeof func != 'function') { throw new TypeError(FUNC_ERROR_TEXT); } var length = partials ? partials.length : 0;//應用的參數個數,不傳爲0 if (!length) { 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);//獲得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) {//若是沒傳partials,調用createBind方法,而且將包裝函數賦值給result 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) {//若是傳了partials,可是沒有傳佔位符,調用createPartial方法,而且將包裝函數賦值給result result = createPartial(func, bitmask, thisArg, partials); } else {//若是傳了partials和holders,調用createHybrid方法 result = createHybrid.apply(undefined, newData); } var setter = data ? baseSetData : setData;//設置data的元數據(暫不分析) return setWrapToString(setter(result, newData), func, bitmask);//給包裝以後的方法添加元數據(用於優化),添加toStirng方法,並返回func(具體實現暫不分析) } module.exports = createWrap;
沒傳partials的時候依賴於createBind方法
//_createBind.js var createCtor = require('./_createCtor'),//建立一個能夠建立函數實例的方法 root = require('./_root');//根元素,node環境爲global,瀏覽器環境爲window var WRAP_BIND_FLAG = 1;//bind位掩碼標識 /** * 建立一個包裝func的方法,調用這個方法能夠使用可選的this對象. * * @private * @param {Function} func 須要包裝的函數. * @param {number} bitmask 位掩碼標識. * @param {*} [thisArg] func的this對象. * @returns {Function} 返回新的包裝函數. */ function createBind(func, bitmask, thisArg) { var isBind = bitmask & WRAP_BIND_FLAG,//是不是bind方法 Ctor = createCtor(func);//用於建立實例的構造器 function wrapper() { var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;//若是是根元素調用,使用func,不然使用Ctor return fn.apply(isBind ? thisArg : this, arguments);//調用fn,傳入this對象和參數 } return wrapper;//返回wrapper方法 } module.exports = createBind;
若是傳入了partials,可是沒有傳入佔位符依賴於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,//是否具備bind標識 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,//設置this爲thisArg fn = isBindKey ? thisBinding[func] : func;//設置fn爲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;
至此,bind方法分析完畢
_.bindKey(object, key, [partials])
建立一個方法,調用object[key]方法,以及提早傳入部分參數
//bindKey.js var baseRest = require('./_baseRest'),//建立具備rest參數的方法 createWrap = require('./_createWrap'),//函數包裝方法 getHolder = require('./_getHolder'),//獲得佔位符標識 replaceHolders = require('./_replaceHolders');//替換佔位符 var WRAP_BIND_FLAG = 1,//bind位掩碼 WRAP_BIND_KEY_FLAG = 2,//bindKey位掩碼 WRAP_PARTIAL_FLAG = 32;//partial位掩碼 /** * * * _.bind.placeholder的值默認爲`_` * * @static * @memberOf _ * @since 0.10.0 * @category Function * @param {Object} object 調用該方法的對象. * @param {string} key 方法的key值. * @param {...*} [partials] 須要提早傳入的參數. * @returns {Function} 返回新的已綁定的方法. * @example * * var object = { * 'user': 'fred', * 'greet': function(greeting, punctuation) { * return greeting + ' ' + this.user + punctuation; * } * }; * * var bound = _.bindKey(object, 'greet', 'hi'); * bound('!'); * // => 'hi fred!' * * object.greet = function(greeting, punctuation) { * return greeting + 'ya ' + this.user + punctuation; * }; * * bound('!'); * // => 'hiya fred!' * * // Bound with placeholders. * var bound = _.bindKey(object, 'greet', _, '!'); * bound('hi'); * // => 'hiya fred!' */ var bindKey = baseRest(function(object, key, partials) {//建立使用rest參數的方法 var bitmask = WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG;//位掩碼爲bind和bindKey if (partials.length) {//若是傳入了partials var holders = replaceHolders(partials, getHolder(bindKey));//獲得佔位符的索引 bitmask |= WRAP_PARTIAL_FLAG;//位掩碼加上partial的標識 } return createWrap(key, bitmask, object, partials, holders);//調用createWrap方法,返回包裝以後的方法 }); // Assign default placeholders. bindKey.placeholder = {}; module.exports = bindKey;
一樣先看createWrap方法
//_createWrap.js var baseSetData = require('./_baseSetData'), createBind = require('./_createBind'),//建立bind方法 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;//是不是bindKey方法(是的) if (!isBindKey && typeof func != 'function') { throw new TypeError(FUNC_ERROR_TEXT); } var length = partials ? partials.length : 0;//應用的參數個數,不傳爲0 if (!length) { 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);//獲得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 {//進入這裏,調用createHybrid方法。 result = createHybrid.apply(undefined, newData); } 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,//是否具備bind標識(有) isBindKey = bitmask & WRAP_BIND_KEY_FLAG,//是否具備BindKey標識(有) 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,//設置this爲thisArg fn = isBindKey ? thisBinding[func] : func;//設置fn爲thisBinding[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;
bindKey方法分析完畢
今天到此結束,一步一個腳印。。