lodash源码阅读三:isLength,isArrayLike,isObjectLike,isArrayLikeObject,map,concat

534 阅读4分钟

参考资料

github仓库地址

源码拷贝仓库地址(阅读的源码版本)

中文文档

英文文档

isLength(value)

判断传入的 value 值是否是一个有效的类数组长度值(大于 -1 小于 Number.MAX_SAFE_INTEGER 的整数)

// Number.MAX_SAFE_INTEGER
const MAX_SAFE_INTEGER = 9007199254740991

/**
 * 判断传入的 value 值是否是一个有效的类数组长度值
 *
 * @param {*} value 
 * @returns {boolean} 
 * @example
 *
 * isLength(3)
 * // => true
 *
 * isLength(Number.MIN_VALUE)
 * // => false
 *
 * isLength(Infinity)
 * // => false
 *
 * isLength('3')
 * // => false
 */
function isLength(value) {
  return typeof value === 'number' &&
    value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER
}

export default isLength

isArrayLike(value)

判断 value 是否是一个类数组。如果 value 是一个类数组,value 就不是一个函数,并且有一个 length 属性,类型为 number,并且是大于 -1 小于 Number.MAX_SAFE_INTEGER 的整数

import isLength from './isLength.js'

/**
 * 判断 value 是否是一个类数组。如果 value 是一个类数组,value 就不是一个函数,并且有一个 length 属性
 * 类型为 number,并且是大于 -1 小于 Number.MAX_SAFE_INTEGER 的整数
 *
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is array-like, else `false`.
 * @example
 *
 * isArrayLike([1, 2, 3])
 * // => true
 *
 * isArrayLike(document.body.children)
 * // => true
 *
 * isArrayLike('abc')
 * // => true
 *
 * isArrayLike(Function)
 * // => false
 */
function isArrayLike(value) {
  return value != null && typeof value !== 'function' && isLength(value.length)
}

export default isArrayLike

isObjectLike(value)

isObjectLike 判断 value 是否是 typeof 值为 'object' 并且 value 不为 null

/**
 * isObjectLike 判断 value 是否是 typeof 值为 'object' 并且 value 不为 null
 *
 * @param {*} value 
 * @returns {boolean} 
 * @example
 *
 * isObjectLike({})
 * // => true
 *
 * isObjectLike([1, 2, 3])
 * // => true
 *
 * isObjectLike(Function)
 * // => false
 *
 * isObjectLike(null)
 * // => false
 */
function isObjectLike(value) {
  return typeof value === 'object' && value !== null
}

export default isObjectLike

isArrayLikeObject(value)

判断 value 是否是类数组对象

import isArrayLike from './isArrayLike.js'
import isObjectLike from './isObjectLike.js'

/**
 * 判断 value 是否是类数组对象
 *
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is an array-like object,
 *  else `false`.
 * @example
 *
 * isArrayLikeObject([1, 2, 3])
 * // => true
 *
 * isArrayLikeObject(document.body.children)
 * // => true
 *
 * isArrayLikeObject('abc')
 * // => false
 *
 * isArrayLikeObject(Function)
 * // => false
 */
function isArrayLikeObject(value) {
  return isObjectLike(value) && isArrayLike(value)
}

export default isArrayLikeObject

map(array, iteratee)

创建一个数组,数组中的元素是 iteratee 函数遍历 array 中每个元素返回的结果。

iteratee 接收三个参数 (value, index | key, array)

/**
 * 创建一个数组,数组中的元素是 iteratee 函数遍历 array 中每个元素返回的结果。
 * iteratee 接受三个参数 (value, index | key, array)
 *
 * @since 5.0.0
 * @category Array
 * @param {Array} array The array to iterate over.
 * @param {Function} iteratee The function invoked per iteration.
 * @returns {Array} Returns the new mapped array.
 * @example
 *
 * function square(n) {
 *   return n * n
 * }
 *
 * map([4, 8], square)
 * // => [16, 64]
 */
function map(array, iteratee) {
  let index = -1
  const length = array == null ? 0 : array.length
  // 创建新数组
  const result = new Array(length)

  while (++index < length) {
    // 传入参数执行 iteratee
    // 并且将结果添加到 result 中
    result[index] = iteratee(array[index], index, array)
  }
  // 返回 result
  return result
}

export default map

cancat(array, values) 以及私有依赖

isArray(value)

isArray 这个方法其实就是直接使用了 Array 的静态方法 isArray

/**
 * 判断 value 是否是数组
 *
 * @param {*} value 要被判断是否是数组的 value
 * @returns {boolean} 返回布尔值
 * @example
 *
 * _.isArray([1, 2, 3]);
 * // => true
 *
 * _.isArray(document.body.children);
 * // => false
 *
 * _.isArray('abc');
 * // => false
 *
 * _.isArray(_.noop);
 * // => false
 */
const isArray = Array.isArray;

export default isArray

copyArray(source, array)

从数组 source 中将元素浅拷贝到另一个数组中

/**
 * 从数组 source 中将元素浅拷贝到另一个数组中
 *
 * @param {Array} source 要拷贝的数组源
 * @param {Array} [array=[]] 添加拷贝元素的数组,默认为空数组
 * @returns {Array} 返回 array
 */
function copyArray(source, array) {
  let index = -1
  const length = source.length

  // 如果没有传入 array,初始化一个空数组,长度和 source 长度相同
  array || (array = new Array(length))
  // 遍历 source ,将元素添加到 array 中
  while (++index < length) {
    array[index] = source[index]
  }
  return array
}

export default copyArray

arrayPush(array, values)

arrayPush 将一个数组中的元素添加到目标数组中(从目标数组的末端开始加入)

/**
 * arrayPush 将一个数组中的元素添加到目标数组中(从目标数组的末端开始加入)
 *
 * @private
 * @param {Array} array 目标数组
 * @param {Array} values 要添加的元素组成的数组
 * @returns {Array} 返回目标数组 array
 */
function arrayPush(array, values) {
  var index = -1, // 初始化索引
      length = values.length, // values 长度
      offset = array.length; // 元素插入位置偏移量

  while (++index < length) {
    // 遍历 values,将元素添加到 array 中
    array[offset + index] = values[index];
  }
  return array;
}

export default arrayPush

isArguments(value)

isArguments 判断 value 是否是 arguments 对象

import getTag from './.internal/getTag.js'
import isObjectLike from './isObjectLike.js'

/**
 * isArguments 判断 value 是否是 arguments 对象
 *
 * @since 0.1.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is an `arguments` object, else `false`.
 * @example
 *
 * isArguments(function() { return arguments }())
 * // => true
 *
 * isArguments([1, 2, 3])
 * // => false
 */
function isArguments(value) {
  return isObjectLike(value) && getTag(value) == '[object Arguments]'
}

export default isArguments

isFlattenable(value)

isFlattenable 判断 value 是否是的 arguments 对象或者是数组,或者是可展开的对象

import isArguments from '../isArguments.js'

/** Built-in value reference. */
const spreadableSymbol = Symbol.isConcatSpreadable

/**
 * isFlattenable 判断 value 是否是的 `arguments` 对象或者是数组,或者是可展开的对象
 *
 * @private
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is flattenable, else `false`.
 */
function isFlattenable(value) {
  return Array.isArray(value) || isArguments(value) ||
    !!(value && value[spreadableSymbol])
}

export default isFlattenable

关于 Symbol.isConcatSpreadable,MDN 文档有详细的说明 Symbol.isConcatSpreadable

baseFlatten(array, depth, predicate, isStrict, result)

baseFlatten 将多维数组中的元素展开添加到指定的目标数组中

import isFlattenable from './isFlattenable.js'

/**
 * baseFlatten 将多维数组中的元素展开添加到指定的目标数组中
 *
 * @param {Array} array 要被展开的数组
 * @param {number} depth 展开的深度
 * @param {boolean} [predicate=isFlattenable] 数组元素的校验
 * @param {boolean} [isStrict] 是否严格执行 predicate 函数的校验
 * @param {Array} [result=[]] 指定的添加元素的目标数组
 * @returns {Array} Returns the new flattened array.
 */
function baseFlatten(array, depth, predicate, isStrict, result) {
  // predicate 默认为 isFlattenable
  predicate || (predicate = isFlattenable)
  // result 默认为空数组
  result || (result = [])

  if (array == null) {
    // 如果 array 不存在,返回空数组
    return result
  }

  for (const value of array) {
    // 遍历 array 数组
    // 如果展开的深度大于 0 并且元素通过了 predicate 函数的校验
    if (depth > 0 && predicate(value)) {
      if (depth > 1) {
        // 如果展开的深度大于 1,递归展开当前元素添加到 result 中
        baseFlatten(value, depth - 1, predicate, isStrict, result)
      } else {
        // 如果展开深度为 1,将元素添加到 result 中
        result.push(...value)
      }
    } else if (!isStrict) {
      // 如果元素没有通过了 predicate 函数的校验
      // isStrict 为 false,将元素添加到 result 中
      result[result.length] = value
    }
  }
  // 返回 result
  return result
}

export default baseFlatten

concat(array, values)

创建一个新数组,并且将 array 和其他值添加到新数组中并返回新数组

import isArray from './isArray'
import copyArray from './.internal/copyArray'
import baseFlatten from './.internal/baseFlatten'
import arrayPush from './.internal/arrayPush'

/**
 * 创建一个新数组,并且将 array 和其他值添加到新数组中并返回新数组
 *
 * @param {Array} array 
 * @param {...*} [values] 
 * @returns {Array} 
 * @example
 *
 * var array = [1];
 * var other = _.concat(array, 2, [3], [[4]]);
 *
 * console.log(other);
 * // => [1, 2, 3, [4]]
 *
 * console.log(array);
 * // => [1]
 */
function concat() {
  // 传入的参数长度
  var length = arguments.length;
  // 如果没有传入任何参数,返回空数组
  if (!length) {
    return [];
  }
  var args = Array(length - 1), // 除了 array 的所有元素集合
      array = arguments[0], // array
      index = length;

  while (index--) {
    // 除了 array 之外的所有参数添加到 args 中
    args[index - 1] = arguments[index];
  }
  // isArray(array) ? copyArray(array) : [array] 如果 array 不是数组,将其作为元素生成数组,如果是数组,将 array 作为数据源拷贝一个新的数组
  // baseFlatten(args, 1) 展开 args 元素组成新数组添加到 array 中
  return arrayPush(isArray(array) ? copyArray(array) : [array], baseFlatten(args, 1));
}

export default concat