lodash里的内部方法createHybrid

157 阅读1分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第31天,点击查看活动详情

前言

lodash里的内部方法createHybrid方法可以创建一个包装func的函数,用可选的this调用它。

在实现上,该方法借助了几个内部方法,我们先逐一了解各个内部方法的实现。

countHolders

countHolders方法可以获取数组中出现的占位符placeholder的数目。

参数说明:

  • 参数1:数组类型,表示要检查的数组。
  • 参数2:任意类型,表示要搜索的占位符。

countHolders方法实现上通过while遍历整个数组,当数组的某一项和占位符placeholder相等时,内部计数+1。

源码如下:

function countHolders(array, placeholder) {
  var length = array.length,
      result = 0;

  while (length--) {
    if (array[length] === placeholder) {
      ++result;
    }
  }
  return result;
}

getHolder

getHolder方法可以获取参func的参数占位符值,该方法接收一个要检查的函数参数func,返回占位符的值。

源码如下:

function getHolder(func) {
  var object = func;
  return object.placeholder;
}

replaceHolders

replaceHolders方法可以用内部占位符替换“数组”中的所有“占位符”元素,同时返回其索引的数组。

源码如下:

var PLACEHOLDER = '__lodash_placeholder__';

function replaceHolders(array, placeholder) {
  var index = -1,
      length = array.length,
      resIndex = 0,
      result = [];

  while (++index < length) {
    var value = array[index];
    if (value === placeholder || value === PLACEHOLDER) {
      array[index] = PLACEHOLDER;
      result[resIndex++] = index;
    }
  }
  return result;
}

reorder

reorder方法主要是根据指定的索引对数组重新排序。

参数说明:

  • 参数1:数组类型,表示要重新排序的数组。
  • 参数2:数组类型,表示要排列的数组索引。

实现上借助copyArray方法和isIndex方法,通过while循环对数组进行遍历判断赋值,其中copyArray方法和isIndex方法在之前的篇章已经介绍过了,主要是复制数组和判断是否属于索引。

源码如下:

import copyArray from './_copyArray.js';
import isIndex from './_isIndex.js';

var nativeMin = Math.min;

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;
}

源码实现

在《 lodash里内部方法mergeData实现 》中我们介绍了composeArgs方法和composeArgsRight方法。

在《 lodash里的内部方法createBind 》中我们介绍了createCtor方法。

在《 lodash里的内部方法createRecurry 》中我们介绍了createRecurry方法。

通过代码判断以及各个方法的相互调用,createHybrid方法源码实现如下:

import composeArgs from './_composeArgs.js';
import composeArgsRight from './_composeArgsRight.js';
import countHolders from './_countHolders.js';
import createCtor from './_createCtor.js';
import createRecurry from './_createRecurry.js';
import getHolder from './_getHolder.js';
import reorder from './_reorder.js';
import replaceHolders from './_replaceHolders.js';
import root from './_root.js';

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;


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[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) {
      args.length = ary;
    }
    if (this && this !== root && this instanceof wrapper) {
      fn = Ctor || createCtor(fn);
    }
    return fn.apply(thisBinding, args);
  }
  return wrapper;
}

小结

本篇章我们了解了createHybrid的实现过程,以及了解了其内部实现上调用的其他方法,同时也了解其他方法的实现。