zoukankan      html  css  js  c++  java
  • lodash源码学习(12)

    _.rearg(func, indexes)

    创建一个func的包装方法,可以指定传入参数作为func的参数的索引。比如指定索引[2,0,1],则传入的第一个参数作为func的第三个参数被调用,第二个参数作为func的第一个参数。。以此类推.
    //rearg.js
    
    var createWrap = require('./_createWrap'),//函数包装方法
        flatRest = require('./_flatRest');//扁平化rest参数,即rearg(fn,[2,0,1]) = rearg(fn, 2, 0, 1)
    
    var WRAP_REARG_FLAG = 256; //rearg的位掩码标识
    
    /**
     *
     * @param {Function} func The 需要包装的方法.
     * @param {...(number|number[])} indexes 指定参数的索引值.
     * @returns {Function} 返回新的方法.
     * @example
     *
     * var rearged = _.rearg(function(a, b, c) {
     *   return [a, b, c];
     * }, [2, 0, 1]);
     *
     * rearged('b', 'c', 'a')
     * // => ['a', 'b', 'c']
     */
     var rearg = flatRest(function(func, indexes) { //支持使用数组或者rest参数形式
      return createWrap(func, WRAP_REARG_FLAG, undefined, undefined, undefined, indexes);//调用包装方法
    });
    
    module.exports = rearg;

    依然是依赖于createWrap和createHybrid方法

    //_createWrap.js
    
    var baseSetData = require('./_baseSetData'),
    createBind = require('./_createBind'),
    createCurry = require('./_createCurry'),
    createHybrid = require('./_createHybrid'),
    createPartial = require('./_createPartial'),
    getData = require('./_getData'),
    mergeData = require('./_mergeData'),
    setData = require('./_setData'),
    setWrapToString = require('./_setWrapToString'),
    toInteger = require('./toInteger');
    
    var FUNC_ERROR_TEXT = 'Expected a function';
    
    //各种方法的位掩码标识
    var WRAP_BIND_FLAG = 1,
    WRAP_BIND_KEY_FLAG = 2,
    WRAP_CURRY_FLAG = 8,
    WRAP_CURRY_RIGHT_FLAG = 16,
    WRAP_PARTIAL_FLAG = 32,
    WRAP_PARTIAL_RIGHT_FLAG = 64;
    
    var nativeMax = Math.max;//原生最大值方法
    
    /**
    * 创建一个函数,该函数可以创建或调用func用可选的this和部分应用的参数.
    *
    * @param {Function|string} func 需要包装的函数.
    * @param {number} bitmask 位掩码标识
    * @param {*} [thisArg] func的this对象
    * @param {Array} [partials] 应用的参数
    * @param {Array} [holders] 占位符的索引
    * @param {Array} [argPos] .
    * @param {number} [ary] .
    * @param {number} [arity] 可用参数数量.
    * @returns {Function} 返回包装之后的函数.
    */
    //lodash使用BitMask来进行各种方法的表示,BitMask使用方法可以看 http://geek.csdn.net/news/detail/73343
    
    function createWrap(func, bitmask, thisArg, partials, holders, argPos, ary, arity) {
    var isBindKey = bitmask & WRAP_BIND_KEY_FLAG;
    if (!isBindKey && typeof func != 'function') {
    throw new TypeError(FUNC_ERROR_TEXT);
    }
    var length = partials ? partials.length : 0;//传入的参数个数,不传为0
    if (!length) {//如果没有传入partials
    bitmask &= ~(WRAP_PARTIAL_FLAG | WRAP_PARTIAL_RIGHT_FLAG);//清除WRAP_PARTIAL_FLAG和WRAP_PARTIAL_RIGHT_FLAG
    partials = holders = undefined;//部分参数和占位符索引都为undefined
    }
    ary = ary === undefined ? ary : nativeMax(toInteger(ary), 0);
    arity = arity === undefined ? arity : toInteger(arity);
    length -= holders ? holders.length : 0;//如果有占位符,参数长度减去占位符长度
    
    if (bitmask & WRAP_PARTIAL_RIGHT_FLAG) {//是否是WRAP_PARTIAL_RIGHT_FLAG(不是,跳过)
    var partialsRight = partials,
        holdersRight = holders;
    
    partials = holders = undefined;
    }
    var data = isBindKey ? undefined : getData(func);
    
    var newData = [
    func, bitmask, thisArg, partials, holders, partialsRight, holdersRight,
    argPos, ary, arity
    ];//将所有参数赋值给newData
    
    if (data) {
    mergeData(newData, data);
    }
    func = newData[0];
    bitmask = newData[1];
    thisArg = newData[2];
    partials = newData[3];
    holders = newData[4];
    arity = newData[9] = newData[9] === undefined
    ? (isBindKey ? 0 : func.length)
    : nativeMax(newData[9] - length, 0);
    
    if (!arity && bitmask & (WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG)) {
    bitmask &= ~(WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG);
    }
    if (!bitmask || bitmask == WRAP_BIND_FLAG) {//
    var result = createBind(func, bitmask, thisArg);
    } else if (bitmask == WRAP_CURRY_FLAG || bitmask == WRAP_CURRY_RIGHT_FLAG) {
    result = createCurry(func, bitmask, arity);
    } else if ((bitmask == WRAP_PARTIAL_FLAG || bitmask == (WRAP_BIND_FLAG | WRAP_PARTIAL_FLAG)) && !holders.length) {
    result = createPartial(func, bitmask, thisArg, partials);
    } else {
    result = createHybrid.apply(undefined, newData);//调用createHybrid
    }
    var setter = data ? baseSetData : setData;//设置data的元数据
    return setWrapToString(setter(result, newData), func, bitmask);//给包装之后的方法添加元数据(用于优化),添加toStirng方法,并返回func
    }
    
    module.exports = createWrap;
    createHybrid
    //_createHybrid.js
    
    var composeArgs = require('./_composeArgs'),//组合参数方法
        composeArgsRight = require('./_composeArgsRight'),
        countHolders = require('./_countHolders'),
        createCtor = require('./_createCtor'),
        createRecurry = require('./_createRecurry'),
        getHolder = require('./_getHolder'),
        reorder = require('./_reorder'),
        replaceHolders = require('./_replaceHolders'),
        root = require('./_root');
    
    //位掩码标识
    var WRAP_BIND_FLAG = 1,
        WRAP_BIND_KEY_FLAG = 2,
        WRAP_CURRY_FLAG = 8,
        WRAP_CURRY_RIGHT_FLAG = 16,
        WRAP_ARY_FLAG = 128,
        WRAP_FLIP_FLAG = 512;
    
    /**
     * 创建一个包装函数,调用func使用可选的thisArg,应用部分参数和柯里化.
     *
     * @param {Function|string} func 需要包装的方法.
     * @param {number} bitmask 位掩码标识
     * @param {*} [thisArg] this对象.
     * @param {Array} [partials] 实现传入的参数.
     * @param {Array} [holders] 占位符.
     * @param {Array} [partialsRight] .
     * @param {Array} [holdersRight] .
     * @param {Array} [argPos] .
     * @param {number} [ary] .
     * @param {number} [arity] 可用函数参数数量.
     * @returns {Function} 返回新的包装函数.
     */
    function createHybrid(func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity) {
      var isAry = bitmask & WRAP_ARY_FLAG,
          isBind = bitmask & WRAP_BIND_FLAG,
          isBindKey = bitmask & WRAP_BIND_KEY_FLAG,
          isCurried = bitmask & (WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG),
          isFlip = bitmask & WRAP_FLIP_FLAG,
          Ctor = isBindKey ? undefined : createCtor(func);
    
      function wrapper() {
        var length = arguments.length,//参数个数
            args = Array(length),//保存所有参数
            index = length;//参数数组索引
    
        while (index--) {//遍历参数,将所有参数存入args
          args[index] = arguments[index];
        }
        if (isCurried) {
          var placeholder = getHolder(wrapper),
              holdersCount = countHolders(args, placeholder);
        }
        if (partials) {
          args = composeArgs(args, partials, holders, isCurried);
        }
        if (partialsRight) {
          args = composeArgsRight(args, partialsRight, holdersRight, isCurried);
        }
        length -= holdersCount;
        if (isCurried && length < arity) {
          var newHolders = replaceHolders(args, placeholder);
          return createRecurry(
            func, bitmask, createHybrid, wrapper.placeholder, thisArg,
            args, newHolders, argPos, ary, arity - length
          );
        }
        var thisBinding = isBind ? thisArg : this,
            fn = isBindKey ? thisBinding[func] : func;
    
        length = args.length;
        if (argPos) {//如果传入了参数位置
          args = reorder(args, argPos); //调用reorder方法 
        } else if (isFlip && length > 1) {
          args.reverse();
        }
        if (isAry && ary < length) {
          args.length = ary;
        }
        if (this && this !== root && this instanceof wrapper) {
          fn = Ctor || createCtor(fn);
        }
        return fn.apply(thisBinding, args);//调用fn,并且传入args
      }
      return wrapper;//返回包装方法
    }
    module.exports = createHybrid;

    可以看到这里依赖于reorder方法,用于重组参数

    //_reorder.js
    
    var copyArray = require('./_copyArray'),//拷贝数组
        isIndex = require('./_isIndex');//是否为索引
    
    
    var nativeMin = Math.min; //原生求最小值放
    
    /**
     * 对数组进行根据指定的索引重新排序.
     *
     * @private
     * @param {Array} array 需要排序的数组.
     * @param {Array} indexes 排序的索引.
     * @returns {Array} 返回这个数组.
     */
    function reorder(array, indexes) {
      var arrLength = array.length,
          length = nativeMin(indexes.length, arrLength),//数组长度
          oldArray = copyArray(array);
    
      while (length--) {//对索引值进行遍历
        var index = indexes[length];//索引值
        array[length] = isIndex(index, arrLength) ? oldArray[index] : undefined; //对数组依次赋值对应索引值的元素
      }
      return array;//返回这个数组
    }
    
    module.exports = reorder;

    _.rest(func, [start=func.length-1])

    创建一个方法,支持rest参数形式.
    这个方法实现了和es6的rest参数一样的效果,形如fn(...args),rest参数简化了使用arguments获取多余参数的方法。lodash中大量的方法都依赖于rest参数,比如上一次学习的partial,partialRight等等。
    //rest.js
    
    var baseRest = require('./_baseRest'),//包装函数使支持rest参数
        toInteger = require('./toInteger'); //转换为整型
    
    
    var FUNC_ERROR_TEXT = 'Expected a function';
    
    /**
     *
     *
     * @param {Function} func 需要包装的方法.
     * @param {number} [start=func.length-1] rest参数的开始位置.
     * @returns {Function} 返回一个新的方法.
     * @example
     *
     * var say = _.rest(function(what, names) {
     *   return what + ' ' + _.initial(names).join(', ') +
     *     (_.size(names) > 1 ? ', & ' : '') + _.last(names);
     * });
     *
     * say('hello', 'fred', 'barney', 'pebbles');
     * // => 'hello fred, barney, & pebbles'
     */
    function rest(func, start) {
      if (typeof func != 'function') {
        throw new TypeError(FUNC_ERROR_TEXT);
      }
      start = start === undefined ? start : toInteger(start); //如果传入了start,将其转换为整型
      return baseRest(func, start);
    }
    
    module.exports = rest;

    依赖于baseRest方法和overRest方法

    baseRest

    // _baseRest.js
    
    var identity = require('./identity'), //返回传入的第一个值
        overRest = require('./_overRest'),
        setToString = require('./_setToString'); //设置元数据和toString方法
    
    /**
     * _rest方法的基本实现.
     *
     * @private
     * @param {Function} func 需要使用rest参数的方法.
     * @param {number} [start=func.length-1] rest参数的开始位置.
     * @returns {Function} 返回新的方法.
     */
    function baseRest(func, start) {
      return setToString(overRest(func, start, identity), func + '');
    }
    
    module.exports = baseRest;

    overRest

    //_overRest.js
    
    var apply = require('./_apply');//同function.apply
    
    var nativeMax = Math.max;
    
    /**
     * rest参数转换方法.
     *
     * @private
     * @param {Function} func 需要包装的方法.
     * @param {number} [start=func.length-1] 指定rest参数的起始位置.
     * @param {Function} transform rest数组转换器.
     * @returns {Function} 返回新的函数.
     */
    function overRest(func, start, transform) {
      start = nativeMax(start === undefined ? (func.length - 1) : start, 0); //限制start在0和总参数长度之间
      return function() {
        var args = arguments,
            index = -1,
            length = nativeMax(args.length - start, 0),
            array = Array(length);
    
        while (++index < length) { //遍历参数,将start之后的参数保存到array中
          array[index] = args[start + index];
        }
        index = -1;
        var otherArgs = Array(start + 1); //调用func的参数
        while (++index < start) {
          otherArgs[index] = args[index]; // start之前的参数为相应传入的参数
        }
        //start位置的参数为array
        otherArgs[start] = transform(array);
        //调用fn,传入otherArgs
        //即如果start为1 ,fn(1,2,3,4) ==> fn(1,[2,3,4])
        return apply(func, this, otherArgs); 
      };
    }
    
    module.exports = overRest;

    _.spread(func, [start=0])

    创建一个方法调用func,传入一个数组作为func的参数,类似apply.
    //_spread.js
    
    var apply = require('./_apply'), //apply方法
        arrayPush = require('./_arrayPush'), //push方法
        baseRest = require('./_baseRest'), //包装函数使支持rest参数
        castSlice = require('./_castSlice'), //slice方法
        toInteger = require('./toInteger'); //转换为整型
    
    /** Error message constants. */
    var FUNC_ERROR_TEXT = 'Expected a function';
    
    var nativeMax = Math.max; //求最大值方法
    
    /**
     *
     * @static
     * @memberOf _
     * @since 3.2.0
     * @category Function
     * @param {Function} func 需要包装的方法.
     * @param {number} [start=0] 需要展开的开始位置.
     * @returns {Function} 返回一个新的函数.
     * @example
     *
     * var say = _.spread(function(who, what) {
     *   return who + ' says ' + what;
     * });
     *
     * say(['fred', 'hello']);
     * // => 'fred says hello'
     *
     * var numbers = Promise.all([
     *   Promise.resolve(40),
     *   Promise.resolve(36)
     * ]);
     *
     * numbers.then(_.spread(function(x, y) {
     *   return x + y;
     * }));
     * // => a Promise of 76
     */
    function spread(func, start) {
      if (typeof func != 'function') {
        throw new TypeError(FUNC_ERROR_TEXT);
      }
      start = start == null ? 0 : nativeMax(toInteger(start), 0); //确保start大于0
      return baseRest(function(args) {
        var array = args[start],
            otherArgs = castSlice(args, 0, start);//将要调用的参数,start之前的参数不变
    
        if (array) {
          arrayPush(otherArgs, array); //如果有start位置的值,传入otherArgs
        }
        return apply(func, this, otherArgs); //调用func,并且将otherArgs作为参数传入
      });
    }
    
    module.exports = spread;

    _.unary(func)

    创建一个方法只接受一个参数,忽略其他所有参数.
    //unary.js
    
    var ary = require('./ary'); //创建一个方法调用func用n个参数,忽略其他所有参数.(见源码学习7)
    
    /**
     * 
     *
     * @param {Function} func 需要处理的函数。
     * @returns {Function} 返回处理后的方法.
     * @example
     *
     * _.map(['6', '8', '10'], _.unary(parseInt));
     * // => [6, 8, 10]
     */
    function unary(func) {
      return ary(func, 1);//调用ary方法,只接受一个参数。
    }
    
    module.exports = unary;

    _.wrap(value, [wrapper=identity])

    创建一个方法接受value和将要调用的方法wapper,并且将value作为wrapper的第一个参数.
    //wrap.js
    
    var castFunction = require('./_castFunction'), //确保为函数
        partial = require('./partial');//partial方法(见lodash源码学习partial,partialRight)
    
    /**
     * 
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Function
     * @param {*} value The value to wrap.
     * @param {Function} [wrapper=identity] The wrapper function.
     * @returns {Function} Returns the new function.
     * @example
     *
     * var p = _.wrap(_.escape, function(func, text) {
     *   return '<p>' + func(text) + '</p>';
     * });
     *
     * p('fred, barney, & pebbles');
     * // => '<p>fred, barney, &amp; pebbles</p>'
     */
    function wrap(value, wrapper) {
      return partial(castFunction(wrapper), value);//调用partial并将value作为第一个参数提前传入
    }
    
    module.exports = wrap;

    lodash--Function篇,学习完毕。

  • 相关阅读:
    山丽防水墙客户端的卸载
    还原冰点密码清除
    STP学习总结
    NTFS权限设置时卡死
    SQL server 2000安装时“以前的某个程序安装已在安装计算机上创建挂起”
    Projecet客户端登陆无法通过验证
    Linux-nftables
    Linux-kernel-timeline
    blog编辑技巧
    Linux-swap
  • 原文地址:https://www.cnblogs.com/wandiao/p/7846664.html
Copyright © 2011-2022 走看看