lodash源码阅读(一):isSymbol, isObject, toNumber, toFinite, toInteger, slice

621 阅读4分钟

参考资料

github仓库地址

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

中文文档

英文文档

isSymbol(value)

getTag(value)

返回 valuetoStringTag,相比Object.prototype.toString,添加了对 nullundefinedtag

const toString = Object.prototype.toString

/**
 * 返回 value 的 toStringTag
 *
 * @param {*} value 传入要获取 tag 的对象
 * @returns {string} 返回对象对应的 tag
 */
function getTag(value) {
  if (value == null) {
    // 如果 value 是 null 或者 undefined,返回对应的 tag 
    return value === undefined ? '[object Undefined]' : '[object Null]'
  }
  return toString.call(value)
}

export default getTag

isSymbol(value)

检测传入的 value 是否是 Symbol 数据类型或者 Symbol 对象

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

/**
 * 检测传入的 `value` 是否是 `Symbol` 数据类型或者 `Symbol` 对象
 *
 * @param {*} value 要被检测的值
 * @returns {boolean} 返回检测结果
 * @example
 *
 * isSymbol(Symbol.iterator)
 * // => true
 *
 * isSymbol('abc')
 * // => false
 */
function isSymbol(value) {
  const type = typeof value
  // || 后面的作用是用来判断 Symbol 的 polyfill 生成的 Symbol 对象
  return type == 'symbol' || (type === 'object' && value != null && getTag(value) == '[object Symbol]')
}

export default isSymbol

检测传入的 valuetypeof 值是否为 object 或者 functionnull 除外

isObject(value)

/**
 * 检测传入的 value 的 typeof 值是否为 object 或者 function,null 除外
 *
 * @param {*} value 要被检测的值
 * @returns {boolean} 返回检测结果
 * @example
 *
 * isObject({})
 * // => true
 *
 * isObject([1, 2, 3])
 * // => true
 *
 * isObject(Function)
 * // => true
 *
 * isObject(null)
 * // => false
 */
function isObject(value) {
  const type = typeof value
  return value != null && (type === 'object' || type === 'function')
}

export default isObject

toNumber(value)

将传入的 value 转换为 number 类型

import isObject from './isObject.js'
import isSymbol from './isSymbol.js'

/** NAN 常量 */
const NAN = 0 / 0

/** 正则匹配字符串开头和结尾处的空字符 */
const reTrim = /^\s+|\s+$/g

/** 正则检测是否是带符号的十六进制字符串 */
const reIsBadHex = /^[-+]0x[0-9a-f]+$/i

/** 正则检测是否是二进制字符串 */
const reIsBinary = /^0b[01]+$/i

/** 正则检测是否是八进制字符串 */
const reIsOctal = /^0o[0-7]+$/i

/** 不依赖与内置 root 的方法引用 */
const freeParseInt = parseInt

/**
 * 将传入的 value 转换为 number 类型
 *
 * @param {*} 要被转换的值
 * @returns {number} 返回转换后的结果
 * @see isInteger, toInteger, isNumber
 * @example
 *
 * toNumber(3.2)
 * // => 3.2
 *
 * toNumber(Number.MIN_VALUE)
 * // => 5e-324
 *
 * toNumber(Infinity)
 * // => Infinity
 *
 * toNumber('3.2')
 * // => 3.2
 */
function toNumber(value) {
  // 判断 value 是否是 number 类型,如果是直接返回 value
  if (typeof value === 'number') {
    return value
  }
  // 判断 value 是否是 Symbol 数据类型或者 Symbol 对象,如果是,返回 NAN
  if (isSymbol(value)) {
    return NAN
  }
  // 判断 value 是否是对象
  if (isObject(value)) {
    // 如果 value 是对象,并且 valueOf 属性是一个 function,执行 valueOf 并且将结果赋值给 other。如果 valueOf 属性不是 function,将 value 赋值给 other
    const other = typeof value.valueOf === 'function' ? value.valueOf() : value
    // 如果 other 是对象,toString 转换为 字符串赋值给 value。如果不是,直接赋值给 value
    value = isObject(other) ? `${other}` : other
  }
  // 如果 value 不是 string 类型
  if (typeof value !== 'string') {
    // 判断 value 是否为 0,如果是直接返回 value,如果不是,转换 value 为 number 类型返回
    return value === 0 ? value : +value
  }
  // 将 value 首尾空字符串去除
  value = value.replace(reTrim, '')
  const isBinary = reIsBinary.test(value)
  // 判断 value 是否是二进制字符串或者是八进制字符串,如果使用 parseInt 方法转换为十进制 number 类型返回
  // 如果是带有符号的十六进制字符串,直接返回 NAN
  // 否则直接将字符串转换成 number 类型返回
  return (isBinary || reIsOctal.test(value))
    ? freeParseInt(value.slice(2), isBinary ? 2 : 8)
    : (reIsBadHex.test(value) ? NAN : +value)
}

export default toNumber

通过阅读源码可以发现,toNumber 如果传入带有符号的二进制,八进制或者十六进制的字符串返回的结果是 NAN

console.log(toNumber('+0x1')) // NAN
console.log(toNumber('-0x1')) // NAN
console.log(toNumber('+0b1')) // NAN
console.log(toNumber('-0b1')) // NAN
console.log(toNumber('+0o1')) // NAN
console.log(toNumber('-0o1')) // NAN

在最后的此符串检测中可以做一个小改动,是上面的例子返回正确的数值

...
const reIsBadHex = /^[+-]0x[0-9a-f]+$/i

const reIsHex = /^[+-]?0x[0-9a-f]+$/i

const reIsBinary = /^[+-]?0b[01]+$/i

const reIsOctal = /^[+-]?0o[0-7]+$/i

const reNumberBase = /0[box]/
...
return (isBinary || reIsOctal.test(value))
  ? freeParseInt(value.replace(reNumberBase, ''), isBinary ? 2 : 8)
  : (reIsHex.test(value) ? freeParseInt(value.replace(reNumberBase, ''), 16) : +value)

toFinite(value)

将传入的 value 值转换为有限数值 number 类型

import toNumber from './toNumber.js'

/** INFINITY 常量 */
const INFINITY = 1 / 0
/** 最大数值 */
const MAX_INTEGER = 1.7976931348623157e+308

/**
 * 将传入的 value 值转换为有限数值 number 类型
 *
 * @param {*} value 要被转换的值
 * @returns {number} 返回结果
 * @example
 *
 * toFinite(3.2)
 * // => 3.2
 *
 * toFinite(Number.MIN_VALUE)
 * // => 5e-324
 *
 * toFinite(Infinity)
 * // => 1.7976931348623157e+308
 *
 * toFinite('3.2')
 * // => 3.2
 */
function toFinite(value) {
   // 判断 value 值是否有效。如果值为 0 或者无效,返回 0
  if (!value) {
    return value === 0 ? value : 0
  }
  // 将值转换为 number 类型
  value = toNumber(value)
  if (value === INFINITY || value === -INFINITY) {
    // 判断值是否为 INFINITY,如果是,返回最大或最小数值
    const sign = (value < 0 ? -1 : 1)
    return sign * MAX_INTEGER
  }
  // 判断 value 是否是 NAN,如果是,返回 0,不是返回 value
  return value === value ? value : 0
}

export default toFinite

toInteger(value)

value 值转换为整型数值(向下取整)

import toFinite from './toFinite.js'

/**
 * 将 value 值转换为整型数值(向下取整)
 *
 * @param {*} value 要被转换的值
 * @returns {number} 返回转换结果
 * @see isInteger, isNumber, toNumber
 * @example
 *
 * toInteger(3.2)
 * // => 3
 *
 * toInteger(Number.MIN_VALUE)
 * // => 0
 *
 * toInteger(Infinity)
 * // => 1.7976931348623157e+308
 *
 * toInteger('3.2')
 * // => 3
 */
function toInteger(value) {
  // 将 value 转换为有限的数值
  const result = toFinite(value)
  // 去除小数点后的数值
  const remainder = result % 1
  // 如果小数点后的数值不为 0,向下取整返回,否则返回 result
  return remainder ? result - remainder : result
}

export default toInteger

slice(array, start, end)

浅拷贝传入的数组 array 中起始位置 start 到结束位置 end (不包括 end )的元素组成数组并返回

/**
 * 浅拷贝传入的数组 array 中起始位置 start 到结束位置 end (不包括 end )的元素组成数组并返回
 *
 * @param {Array} array 要被截取元素的数组
 * @param {number} [start=0] 截取起始位置,默认为 0
 * @param {number} [end=array.length] 截取的结束位置,默认为数组的长度
 * @returns {Array} 返回截取后的数组
 * @example
 *
 * var array = [1, 2, 3, 4]
 *
 * _.slice(array, 2)
 * // => [3, 4]
 */
function slice(array, start, end) {
  // 数组的长度,没有传入数组或者数组的长度为 0 返回空数组
  let length = array == null ? 0 : array.length
  if (!length) {
    return []
  }
  // 起始位置和结束位置,如果没有传入,使用默认值
  start = start == null ? 0 : start
  end = end === undefined ? length : end

  if (start < 0) {
    // 如果 start 小于 0,从数组的末端倒数起始位置
    start = -start > length ? 0 : (length + start)
  }
  // 如果 end 大于 length,将 length 赋值 end
  end = end > length ? length : end
  if (end < 0) {
    // 如果 end 小于 0,从数组的末端倒数结束位置
    end += length
  }
  // 计算将要截取的数组长度
  length = start > end ? 0 : ((end - start) >>> 0)
  // start 向下取整
  start >>>= 0

  let index = -1
  const result = new Array(length)
  while (++index < length) {
    result[index] = array[index + start]
  }
  // 返回截取后的数组
  return result
}

export default slice