硬刚 lodash 源码之路,_.chunk

栏目: IT技术 · 发布时间: 4年前

内容简介:你可能需要一些 JavaScript 基础知识才能看懂一些没有注释的细节。判断是否为 Object 类型

前置

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


以上所述就是小编给大家介绍的《硬刚 lodash 源码之路,_.chunk》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

算法导论

算法导论

[美] Thomas H. Cormen、Charles E. Leiserson、Ronald L. Rivest、Clifford Stein / 高等教育出版社 / 2002-5 / 68.00元

《算法导论》自第一版出版以来,已经成为世界范围内广泛使用的大学教材和专业人员的标准参考手册。 这本书全面论述了算法的内容,从一定深度上涵盖了算法的诸多方面,同时其讲授和分析方法又兼顾了各个层次读者的接受能力。各章内容自成体系,可作为独立单元学习。所有算法都用英文和伪码描述,使具备初步编程经验的人也可读懂。全书讲解通俗易懂,且不失深度和数学上的严谨性。第二版增加了新的章节,如算法作用、概率分析......一起来看看 《算法导论》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器