• lodash源码学习(7)


    继续学习lodash,下面是Date篇,Date篇只有一个方法

    “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个参数,忽略其他所有参数.

    //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方法相关的部分

    //_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对象和提前传入部分参数.

    //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方法分析完毕

    今天到此结束,一步一个脚印。。

  • 相关阅读:
    2019最新windows 10永久激活码 win10专业版密钥 win10通用序列号
    安装Office2016遇到“无法流式传输Office”问题
    windows cannot find powershell.exe windows 7
    AI illustrator 如何裁剪图片(扣取局部区域)
    64位 windows2008 R2 上安装32位oracle 10g 的方法
    计算器进行进制数之间的换算
    Linux服务器上监控网络带宽的18个常用命令
    iOS 如何判断一个点在圆、方框、三角形区域内?
    CircularSlider半弧形滑动条
    iOS圆弧渐变进度条的实现
  • 原文地址:https://www.cnblogs.com/wandiao/p/7162714.html
Copyright © 2020-2023  润新知