giscafer / giscafer.github.io

giscafer's blog
http://blog.giscafer.com
10 stars 5 forks source link

lodash 源码学习——castArray、ceil、chunk、clamp 函数 #30

Open giscafer opened 6 years ago

giscafer commented 6 years ago

内容

_.castArray_.ceil_.chunk_.clamp

源码学习

_.castArray

此函数比较简单


/**
 * 将 `value`转为数组(如果`value`不是数组的话)
 * @since 4.4.0
 * @category Lang
 * @param {*} value The value to inspect.
 * @returns {Array} Returns the cast array.
 * @example
 *
 * castArray(1)
 * // => [1]
 *
 * castArray({ 'a': 1 })
 * // => [{ 'a': 1 }]
 *
 * castArray('abc')
 * // => ['abc']
 *
 * castArray(null)
 * // => [null]
 *
 * castArray(undefined)
 * // => [undefined]
 *
 * castArray()
 * // => []
 *
 * const array = [1, 2, 3]
 * console.log(castArray(array) === array)
 * // => true
 */
function castArray(...args) {
  if (!args.length) {
    return []
  }
  // 取出第一个参数,判断如果不是数组则转为数组形式
  const value = args[0];
  return Array.isArray(value) ? value : [value]
}

export default castArray

./.internal/createRound.js


/**
 * Creates a function like `round`.
 * 创建一个类似 `round` 的函数
 * @private
 * @param {string} methodName 四舍五入时使用的 `Match` 的方法名称。
 * @returns {Function} 返回一个新的函数
 */
function createRound(methodName) {
  // 取得 Math 中的函数
  const func = Math[methodName];
  // 返回一个匿名函数
  return (number, precision) => {
    // 默认为0 和控制最大值为292
    precision = precision == null ? 0 : Math.min(precision, 292);
    // precision!==0
    if (precision) {
      // 用指数符号变换以避免浮点问题
      // See [MDN](https://mdn.io/round#Examples) for more details.
      // 这里`${number}e`加入了e字符,为了下边能直接使用pair[1]取值而不需要判断是否存在
      let pair = `${number}e`.split('e');
      // 先算出e `precision` 科学计算结果
      const value = func(`${pair[0]}e${+pair[1] + precision}`)
      // 再恢复原来的位数
      pair = `${value}e`.split('e')
      return +`${pair[0]}e${+pair[1] - precision}`
    }
    // 没有参数直接Math[methodName](number)
    return func(number)
  }
}

export default createRound

_.ceil

改函数主要功能为 createRound.js

import createRound from './.internal/createRound.js'

/**
 * 根据精度计算精确的数值,向上一位取整,类似Math.ceil(不支持precision参数)
 *
 * @since 3.10.0
 * @category Math
 * @param {number} number The number to round up.
 * @param {number} [precision=0] The precision to round up to.
 * @returns {number} Returns the rounded up number.
 * @example
 *
 * ceil(4.006)
 * // => 5
 *
 * ceil(6.004, 2)
 * // => 6.01
 *
 * ceil(6040, -2)
 * // => 6100
 */
const ceil = createRound('ceil')

export default ceil

_.chunk

import slice from './slice.js'

/**
 * Creates an array of elements split into groups the length of `size`.
 * If `array` can't be split evenly, the final chunk will be the remaining
 * elements.
 * 将一个数组拆分为 `size` 长度的数组,如果`array`不能拆分,则最后一个 chunk 将是剩余元素组成。
 *
 * @since 3.0.0
 * @category Array
 * @param {Array} array The array to process.
 * @param {number} [size=1] The length of each chunk
 * @returns {Array} Returns the new array of chunks.
 * @example
 *
 * chunk(['a', 'b', 'c', 'd'], 2)
 * // => [['a', 'b'], ['c', 'd']]
 *
 * chunk(['a', 'b', 'c', 'd'], 3)
 * // => [['a', 'b', 'c'], ['d']]
 */
function chunk(array, size) {
  // 避免size为负数情况
  size = Math.max(size, 0)
  const length = array == null ? 0 : array.length
  if (!length || size < 1) {
    return []
  }
  let index = 0
  let resIndex = 0;
  // 定义结果数组,长度为 Math.ceil(length / size)
  const result = new Array(Math.ceil(length / size))

  while (index < length) {
    // 依次 slice 数组
    result[resIndex++] = slice(array, index, (index += size))
  }
  return result
}

export default chunk

_.clamp

/**
 * Clamps `number` within the inclusive `lower` and `upper` bounds.
 * 在  `lower` 和 `upper` 两个数值范围界限下,取得合适的  `number`  
 * @since 4.0.0
 * @category Number
 * @param {number} number The number to clamp.
 * @param {number} lower The lower bound.
 * @param {number} upper The upper bound.
 * @returns {number} Returns the clamped number.
 * @example
 *
 * clamp(-10, -5, 5)
 * // => -5
 *
 * clamp(10, -5, 5)
 * // => 5
 */
function clamp(number, lower, upper) {
  // 都转为数值型
  number = +number
  lower = +lower
  upper = +upper
  // 排除lower、upper 为 NaN
  lower = lower === lower ? lower : 0
  upper = upper === upper ? upper : 0
  // 排除number 为 NaN
  if (number === number) {
    // 取出三个数值中中间值
    number = number <= upper ? number : upper
    number = number >= lower ? number : lower
  }
  return number
}

export default clamp

总结

_.castArray_.ceil_.chunk_.clamp 这几个模块代码都比较简单,都为简单的属性计算和类型判断,对于非数值参数的判断,我们在_.clamp 函数也可以看到先用 + 号转为 number 型,最后再判断是否对于自己本身的方式 来排除了所有非数值型&NaN的情况。同样的判断方式在 _.isNumber 函数里边我们页可以简单。


源码+测试代码见lodash-sourcecode-study 前端学堂:felearn.com