19

硬刚 lodash 源码之路,_.chunk

 4 years ago
source link: http://www.cnblogs.com/guangzan/p/13233111.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

前置

chunk 函数内部借助其他函数实现,所以从其他函数开始, chunk 在最后。

你可能需要一些 JavaScript 基础知识才能看懂一些没有注释的细节。

isObject

判断是否为 Object 类型

/**
 * Checks if `value` is the
 * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
 * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
 *
 * @since 0.1.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is an object, else `false`.
 * @example
 *
 * isObject({})
 * // => true
 *
 * isObject([1, 2, 3])
 * // => true
 *
 * isObject(Function)
 * // => true
 *
 * isObject(null)
 * // => false
 */
function isObject(value) {
  const type = typeof value
  // 将 function 作为 Object 类型
  return value != null && (type === 'object' || type === 'function')
}

export default isObject

getTag

getTag 获取给定值的 toStringTag

Symbol.toStringTag 是一个内置 symbol,它通常作为对象的属性键使用,对应的属性值应该为字符串类型,这个字符串用来表示该对象的自定义类型标签,通常只有内置的 Object.prototype.toString() 方法会去读取这个标签并把它包含在自己的返回值里。

许多内置的 JavaScript 对象类型即便没有 toStringTag 属性,也能被 toString() 方法识别并返回特定的类型标签,比如:

Object.prototype.toString.call('foo');     // "[object String]"
Object.prototype.toString.call([1, 2]);    // "[object Array]"
Object.prototype.toString.call(3);         // "[object Number]"
Object.prototype.toString.call(true);      // "[object Boolean]"
Object.prototype.toString.call(undefined); // "[object Undefined]"
Object.prototype.toString.call(null);      // "[object Null]"
// ... and more

另外一些对象类型则不然, toString() 方法能识别它们是因为引擎为它们设置好了 toStringTag 标签:

Object.prototype.toString.call(new Map());       // "[object Map]"
Object.prototype.toString.call(function* () {}); // "[object GeneratorFunction]"
Object.prototype.toString.call(Promise.resolve()); // "[object Promise]"
// ... and more

对于你自己创建的类, toString() 找不到 toStringTag 属性时只好返回默认的 Object 标签:

class ValidatorClass {}

Object.prototype.toString.call(new ValidatorClass()); // "[object Object]"

加上 toStringTag 属性,你的类也会有自定义的类型标签了:

class ValidatorClass {
  get [Symbol.toStringTag]() {
    return "Validator";
  }
}

Object.prototype.toString.call(new ValidatorClass()); // "[object Validator]"
const toString = Object.prototype.toString

/**
 * Gets the `toStringTag` of `value`.
 *
 * @private
 * @param {*} value The value to query.
 * @returns {string} Returns the `toStringTag`.
 */
function getTag(value) {

  // 处理 value 能够转化为 null 的值
  if (value == null) {
    return value === undefined ? '[object Undefined]' : '[object Null]'
  }
  return toString.call(value)
}

export default getTag

isSymbol

_.isSymbol(value)

检查 value 是否是 原始 Symbol 或者对象

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

/**
 * Checks if `value` is classified as a `Symbol` primitive or object.
 *
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
 * @example
 *
 * isSymbol(Symbol.iterator)
 * // => true
 *
 * isSymbol('abc')
 * // => false
 */
function isSymbol(value) {
  // typeof 比 Object.prototype.toString 效率高
  const type = typeof value
  return type == 'symbol' || (type === 'object' && value != null && getTag(value) == '[object Symbol]')
}

export default isSymbol

toNumber

_.toNumber(value)

转换 value 为一个 数字

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

/** Used as references for various `Number` constants. */
const NAN = 0 / 0

/** Used to match leading and trailing whitespace. */
const reTrim = /^\s+|\s+$/g

/** Used to detect bad signed hexadecimal string values. */
// 用于检测错误的有符号十六进制字符串值
const reIsBadHex = /^[-+]0x[0-9a-f]+$/i

/** Used to detect binary string values. */
// 二进制。
const reIsBinary = /^0b[01]+$/i

/** Used to detect octal string values. */
// 八进制
const reIsOctal = /^0o[0-7]+$/i

/** Built-in method references without a dependency on `root`. */
// 不依赖于 root 的内置方法引用
// 防止全局作用域下的parseInt被用户替换
const freeParseInt = parseInt

/**
 * Converts `value` to a number.
 *
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to process.
 * @returns {number} Returns the 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) {
  if (typeof value === 'number') {
    return value
  }
  if (isSymbol(value)) {
    return NAN // Number 的引用
  }

  // Object.prototype.valueOf() 方法返回指定对象的原始值
  // 默认情况下,valueOf方法由Object后面的每个对象继承。 
  // 每个内置的核心对象都会覆盖此方法以返回适当的值。
  // 如果对象没有原始值,则valueOf将返回对象本身。
  if (isObject(value)) {
     // value 没有 valueOf 函数或者 valueOf 函数返回一个对象,
     // 将 other 转换成 string 类型,留待后面处理。
    const other = typeof value.valueOf === 'function' ? value.valueOf() : value
    value = isObject(other) ? `${other}` : other
  }
  if (typeof value !== 'string') {
    return value === 0 ? value : +value
  }
  // @example
  // const a = function() {}
  // console.log(a.valueOf()); 
  // -> [Function: a]
  // console.log(typeof a.valueOf());  
  // -> function
  
  // @example
  // const a = {}
  // console.log(a.valueOf(); 
  // -> {}
  
  // 16进制返回NAN
  // 10进制数(+)确保返回值是数值类型
  value = value.replace(reTrim, '') // 用''替换掉字符串中符合reTrim的项
  const isBinary = reIsBinary.test(value) // 二进制
  return (isBinary || reIsOctal.test(value)) // 二进制或八进制
    ? freeParseInt(value.slice(2), isBinary ? 2 : 8) // 删除字符串前两位并解析为十进制的整数
    : (reIsBadHex.test(value) ? NAN : +value) // 十六进制字符串值返回 NAN,否则返回十进制(+)
}

export default toNumber

toFinite

_.toFinite(value)

转换 value 为一个 有限数字

import toNumber from './toNumber.js'

/** Used as references for various `Number` constants. */
const INFINITY = 1 / 0 // 无穷
const MAX_INTEGER = 1.7976931348623157e+308 // 最大整数

/**
 * Converts `value` to a finite number.
 *
 * @since 4.12.0
 * @category Lang
 * @param {*} value The value to convert.
 * @returns {number} Returns the converted 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) {
  // undefined & null -> 0
  if (!value) {
    return value === 0 ? value : 0
  }
  value = toNumber(value)
  // 正负无穷取正负最大值
  if (value === INFINITY || value === -INFINITY) {
    const sign = (value < 0 ? -1 : 1)
    return sign * MAX_INTEGER
  }
  return value === value ? value : 0
}

export default toFinite

toInteger

_.toInteger(value)

转换 value 为一个 整数

import toFinite from './toFinite.js'

/**
 * Converts `value` to an integer.
 *
 * **Note:** This method is loosely based on
 * [`ToInteger`](http://www.ecma-international.org/ecma-262/7.0/#sec-tointeger).
 *
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to convert.
 * @returns {number} Returns the converted integer.
 * @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) {
  const result = toFinite(value)

  // result 为小数时,则 remainder 不为 0
  const remainder = result % 1 // 余数

  // 抹掉小数位
  return remainder ? result - remainder : result
}

export default toInteger

chunk

_.chunk(array, [size=1])

将数组 拆分 成多个 size 长度的区块,并将这些区块组成一个新数组。 如果array 无法被分割成全部等长的区块,那么最后剩余的元素将组成一个区块。

import slice from './slice.js'
import toInteger from './toInteger.js'

/**
 * @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 = 1) {
  // 令 size >= 0
  size = Math.max(toInteger(size), 0)


  const length = array == null ? 0 : array.length
  if (!length || size < 1) {
    return []
  }

  // 构建新数组
  let index = 0
  let resIndex = 0
  const result = new Array(Math.ceil(length / size))
  while (index < length) {
    result[resIndex++] = slice(array, index, (index += size))
  }

  return result
}

export default chunk

参考资料:MDN


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK