Jansiel Notes

【源码学习】Radash(一)Typed 类型判断

Radash

Radash,这个新兴的工具库,以其现代化的设计和对 TypeScript 的原生支持,迅速吸引了开发者的注意。虽然 Radash 是新项目,最近刷很多文章都有推荐,作为 Lodash 的替代,它在 GitHub 上的 star 涨幅很快 ,这表明它是一个有前途的工具库。大家都说好用,而且支持TS,那么我们就来一探究竟吧!

【源码学习】Radash(二)String

Radash 的特点包括:

  • 零依赖:Radash 不依赖于任何第三方库,使得项目更加轻量级
  • TypeScript 友好:Radash 完全使用 TypeScript 编写,提供了准确的类型定义
  • 现代化功能:Radash 去除了 Lodash 中一些过时的函数,并引入了许多新的实用功能
  • 易于理解和维护:Radash 的源代码易于理解,对新手友好

最主要是看着简单,很容易学习 ~ 现在分的类型也不多, VueUse的坑还没完,这又开新坑了…… 哈哈,先干再说

源码确实很简单,很容易上手和学习,写这样一个系列也不会有厌烦情绪了。
配合着官方例子,加了点自己的解读,若有错误还请批评指出,谢谢(抱拳)!~

Typed

typed.ts 类型判断,也作为整个库的工具方法会被其他文件频繁调用 ~ 在项目中封装这些类型判断可高度借鉴

isArray

  • 基本用法
    传入一个值并获取一个布尔值,告诉您该值是否是数组。
1import { isArray } from 'radash'
2
3isArray('hello') // => false
4isArray(['hello']) // => true
5
  • 源码解析
1// 当一个变量 isArray 被赋值为 Array.isArray 时
2// 它实际上引用的是 Array 构造函数 ( class Array 上的静态方法 )
3export const isArray = Array.isArray
4// Array.hasOwnProperty('isArray') -> true
5

isDate

确定值是否为日期

  • 基本用法
    确定值是否为日期。不检查输入日期是否有效,仅检查它是 Javascript 日期类型。
1import { isDate } from 'radash'
2
3isDate(new Date()) // => true
4isDate(12) // => false
5isDate('hello') // => false
6
  • 源码分析
1export const isDate = (value: any): value is Date => {
2  // 使用 Object.prototype.toString.call(value) 方法
3  // 来获取给定值(value)的类型字符串
4  // 然后检查该字符串是否等于'[object Date]'
5  return Object.prototype.toString.call(value) === '[object Date]'
6}
7

由于这段代码依赖于 Object.prototype.toString 方法,因此不能在非 ES6 环境中运行。在非 ES6 环境中,你可以使用其他方法来实现相同的功能,例如使用 typeof value === 'object' 来检查值是否是一个对象,然后使用 value instanceof Date 来检查值是否是 Date 类的实例

isEmpty

判断一个值是否为空

  • 基本用法

这个函数主要用于处理复杂类型的值,包括布尔值、null、undefined、数字、日期、函数、符号等

1import { isEmpty } from 'radash'
2
3isEmpty([]) // => true
4isEmpty('') // => true
5
6isEmpty('hello') // => false
7isEmpty(['hello']) // => false
8
  • 源码解析
 1export const isEmpty = (value: any) => {
 2  // 首先,检查value是否为 布尔值 亦或是 null 或 undefined
 3  // 如果是,则返回true
 4  if (value === true || value === false) return true
 5  if (value === null || value === undefined) return true
 6
 7  // 然后,检查value是否为数字
 8  // 若是,判断 value是否等于0 返回该布尔值
 9  if (isNumber(value)) return value === 0
10
11  // 检查value是否为日期,如果是 value.getTime() 会转换为时间戳
12  // 时间戳若有效的,isNaN 为 false,否则为 true
13  if (isDate(value)) return isNaN(value.getTime())
14
15  // 检查value是否为函数,如果是,则返回false
16  if (isFunction(value)) return false
17
18  // 检查value是否为 Symbol,如果是,则返回false
19  if (isSymbol(value)) return false
20
21  // 获取value的长度或大小(如果有的话),并检查其是否为0
22  // 为0,则说明是空数组,返回true,否则为 false
23  const length = (value as any).length
24  if (isNumber(length)) return length === 0
25  // 同上,这里针对 Map、Set
26  const size = (value as any).size
27  if (isNumber(size)) return size === 0
28
29  const keys = Object.keys(value).length
30  return keys === 0
31}
32

这段代码使用了类型断言(as any),这可能会导致运行时错误,因为类型断言会跳过类型检查。建议在使用时尽可能地进行类型检查和验证。
这段代码没有处理循环引用的情况,如果 value 是一个对象,并且对象之间存在循环引用,那么 Object.keys(value)可能会导致栈溢出错误。

isEqual

确定两个值是否相等

  • 基本用法

给定两个值,如果它们相等则返回 true。( 值比较)主要目的是实现一个通用且高效的比较器,可以用来比较任何类型的数据,包括对象、数组、字符串、数字、布尔值等。

1import { isEqual } from 'radash'
2
3isEqual(null, null) // => true
4isEqual([], []) // => true
5
6isEqual('hello', 'world') // => false
7isEqual(22, 'abc') // => false
8
  • 源码解析
 1export const isEqual = <TType>(x: TType, y: TType): boolean => {
 2  // Object.is方法来检查x和y是否是同一个对象
 3  if (Object.is(x, y)) return true
 4
 5  // 分别检查x和y是否是Date对象。如果是,则比较它们的日期时间戳是否相等
 6  if (x instanceof Date && y instanceof Date) {
 7    return x.getTime() === y.getTime()
 8  }
 9
10  // 检查x和y是否是正则对象。如果是,则比较它们的内部表示是否相等。
11  if (x instanceof RegExp && y instanceof RegExp) {
12    return x.toString() === y.toString()
13  }
14
15  // 检查x和y是否不是对象或者null。如果是,则返回false
16  if (typeof x !== 'object' || x === null || typeof y !== 'object' || y === null) {
17    return false
18  }
19
20  // 这里稍微复杂些
21  // 如果x和y是对象,则获取它们自己的属性键数组,并遍历这些属性键
22  const keysX = Reflect.ownKeys(x as unknown as object) as (keyof typeof x)[]
23  const keysY = Reflect.ownKeys(y as unknown as object)
24
25  // 检查y是否具有相同的属性键,并且对应的属性值是否与x相等。
26  if (keysX.length !== keysY.length) return false
27  for (let i = 0; i < keysX.length; i++) {
28    if (!Reflect.has(y as unknown as object, keysX[i])) return false
29    if (!isEqual(x[keysX[i]], y[keysX[i]])) return false
30  }
31  // 如果所有属性都相等,则返回true。
32  return true
33}
34

isFloat

判断一个值是否为浮点数

  • 基本用法
    传入一个值并获取一个布尔值,告诉您该值是否为浮点型。
1import { isFloat } from 'radash'
2
3isFloat(12.233) // => true
4isFloat(12) // => false
5isFloat('hello') // => false
6
  • 源码解析
1export const isFloat = (value: any): value is number => {
2  // isNumber 函数会检查传入的值是否为 Number 类型,包括整数和浮点数
3  // 使用逻辑运算符 % 检查 value 是否为整数
4  return isNumber(value) && value % 1 !== 0
5  // 如果 value 是浮点数,它将不会被整除
6}
7

isFunction

判断一个值是否是一个函数

  • 基本用法
1import { isFunction } from 'radash'
2
3isFunction('hello') // => false
4isFunction(['hello']) // => false
5isFunction(() => 'hello') // => true
6
  • 源码解析
1export const isFunction = (value: any): value is Function => {
2  // 通过检查value是否为null或undefined,
3  // 然后检查value是否有constructor属性,
4  // 并判断其call和apply方法是否为函数
5  return !!(value && value.constructor && value.call && value.apply)
6}
7

虽然这个函数在大多数情况下能够正确判断传入的值是否为函数,但在某些特殊情况(如 null 或 undefined)中,它会返回 true,这可能不是预期的行为。在处理这些特殊情况时,可能需要额外的检查。

isInt

判断一个值是否为 int

  • 基本用法

这个函数可以用于检查变量是否为有效的整数,从而避免在代码中出现非预期的整数行为。
例如,在处理数字字符串或从用户输入中获取数据时,可以使用 isInt 函数来确保输入的是一个整数。

1import { isInt } from 'radash'
2
3isInt(12) // => true
4isInt(12.233) // => false
5isInt('hello') // => false
6
  • 源码解析
1export const isInt = (value: any): value is number => {
2  // 1. 使用isNumber函数检查value是否为数字(包括整数和浮点数)
3  // 2. 使用逻辑运算符&&连接isNumber(value)和value % 1 === 0
4  // 3. 如果value是数字且是整数,则返回true,否则返回false
5  return isNumber(value) && value % 1 === 0
6}
7
  • isInt 函数不能检查负数和 0 是否为整数,因为它只检查正整数。要检查一个值是否为负整数,可以使用 value < 0 && value % 1 !== 0。
  • isInt 函数不能处理非数字类型的值,例如 null、undefined、string、object 等。在处理这些值时,需要先检查它们是否为数字,然后再使用 isInt 函数。

isNumber

判断一个值是否是数字

  • 基本用法
    在需要检查一个变量是否为数字的情况下,可以使用这个函数进行判断,可以避免使用原始值进行比较时可能出现的类型转换错误。
1import { isNumber } from 'radash'
2
3isNumber('hello') // => false
4isNumber(['hello']) // => false
5isNumber(12) // => true
6
  • 源码解析
 1export const isNumber = (value: any): value is number => {
 2  try {
 3    // 使用Number函数将value转换为数字
 4    // 如果转换后的数字与原始值相等,则返回true,否则返回false
 5    return Number(value) === value
 6  } catch {
 7    return false
 8  }
 9}
10

这个函数会尝试将 value 转换为数字,如果转换失败(例如,如果 value 是一个字符串且包含非数字字符),将抛出一个异常。因此,在实际使用中,需要捕获可能的异常。

isObject

判断一个值是否是一个对象

  • 基本用法

    这个函数可以用于检查变量是否为对象,例如在处理对象属性时,以确保它们不是全局变量或未定义。

1import { isObject } from 'radash'
2
3isObject('hello') // => false
4isObject(['hello']) // => false
5isObject(null) // => false
6isObject({ say: 'hello' }) // => true
7
  • 源码解析
 1export const isObject = (value: any): value is object => {
 2  // 使用逻辑与运算符&&首先检查value是否存在,
 3  // 并且它的构造函数是否为Object
 4  // 这是为了确保接下来要检查的是一个对象
 5
 6  // 然后,使用value.constructor === Object
 7  // 来检查给定的value是否确实是一个对象。
 8  return !!value && value.constructor === Object
 9  // 这是为了区分普通对象和数组
10  // (因为数组的构造函数也是Object)
11}
12

isPrimitive

检查给定值是否为原始值.

现在有7种基本类型了: number 、 string 、 boolean 、 symbol、 bigint、 undefined、 null。

  • 基本用法

    在某些情况下,需要判断给定的值是否为原始类型,以避免处理非预期类型的数据。
    例如,在 JavaScript 中,有些函数和方法接受原始类型作为参数,但拒绝接受对象或函数作为参数。在这种情况下,可以使用 isPrimitive 函数来检查输入值是否为原始类型。

1import { isPrimitive } from 'radash'
2
3isPrimitive(22) // => true
4isPrimitive('hello') // => true
5isPrimitive(['hello']) // => false
6
  • 源码解析
 1// 用于判断给定的值是否为原始类型
 2export const isPrimitive = (value: any): boolean => {
 3  return (
 4    // 首先,检查value是否为undefined,如果是,则返回true。
 5    value === undefined ||
 6    // 然后,检查value是否为null,如果是,则返回true。
 7    value === null ||
 8    (typeof value !== 'object' && typeof value !== 'function')
 9    // 接下来,检查value的类型是否不是对象(typeof value !== 'object')
10    // 也不是函数(typeof value !== 'function')
11    // 如果是,则返回true。
12    // 如果value的类型满足上述条件,则返回false
13  )
14}
15

isPromise

确定一个值是否是 Promise

  • 基本用法

在异步编程中,经常需要检查某个值是否是一个 Promise 实例。例如,当你需要在一个函数中等待一个 Promise 的结果时,你需要在函数的参数列表中使用 await 关键字。

但是,如果你传入了一个不是 Promise 实例的值,那么它会尝试执行这个值,这可能会导致错误。因此,使用 isPromise 函数来检查传入的值是否是一个 Promise 实例,可以避免潜在的错误。

1import { isPromise } from 'radash'
2
3isPromise('hello') // => false
4isPromise(['hello']) // => false
5isPromise(new Promise(res => res())) // => true
6
  • 源码解析
 1export const isPromise = (value: any): value is Promise<any> => {
 2  if (!value) return false
 3  // 如果传入的值是一个Promise实例,那么它应该有一个then方法,且then方法应该是一个函数
 4  // 通过检查value.then是否是一个函数,可以判断传入的值是否是一个Promise实例
 5  if (!value.then) return false
 6  // 使用了一个辅助函数isFunction来检查传入的值是否是一个函数
 7  if (!isFunction(value.then)) return false
 8  return true
 9}
10

这个函数假设所有的传入值都可以被安全地转换为布尔值。
在实际应用中,你可能需要对一些特定的值进行特殊处理,以确保它们不会被错误地识别为 Promise。
例如,对于 null 或 undefined,你可能需要编写额外的逻辑来处理这些情况。

isString

判断一个值是否是一个字符串

  • 基本用法

可以使用这个函数来检查一个值是否为字符串类型,从而避免在代码中使用 instanceof 关键字,这样可以提高代码的可读性和安全性。

1import { isString } from 'radash'
2
3isString('hello') // => true
4isString(['hello']) // => false
5
  • 源码解析
1export const isString = (value: any): value is string => {
2  // 使用 typeof 和 instanceof 运算符,以及对 value 进行非空检查
3  return typeof value === 'string' || value instanceof String
4}
5

isSymbol

确定一个值是否是一个 Symbol

  • 基本用法

这个函数通常用于检查变量是否是一个已经定义的 Symbol,例如使用 const 定义的符号。这对于确保符号名称不会被重复使用非常有用,特别是当符号名称对于应用程序至关重要时。

1import { isSymbol } from 'radash'
2
3isSymbol('hello') // => false
4isSymbol(Symbol('hello')) // => true
5
  • 源码解析
1export const isSymbol = (value: any): value is symbol => {
2  // 函数使用逻辑与(&&)操作符来检查value
3  // 是否为undefined或null,以及value是否具有Symbol构造函数
4  // 如果满足这两个条件,函数返回true,否则返回false。
5  return !!value && value.constructor === Symbol
6}
7

最后

感谢读到这里,谢谢支持。感兴趣可以点个赞呀,会坚持更新完的。