zoukankan      html  css  js  c++  java
  • 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方法分析完毕

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

  • 相关阅读:
    DB2 for Z/os Statement prepare
    Foreign key (referential) constraints on DB2 LUW v105
    复制Informational constraints on LUW DB2 v105
    DB2 SQL Mixed data in character strings
    DB2 create partitioned table
    MVC中使用EF的技巧集(一)
    Asp.Net MVC 开发技巧(二)
    Linq使用技巧及查询示例(一)
    Asp.Net MVC 开发技巧(一)
    Asp.Net MVC Identity 2.2.1 使用技巧(八)
  • 原文地址:https://www.cnblogs.com/wandiao/p/7162714.html
Copyright © 2011-2022 走看看