乐趣区

关于javascript:封装小技巧is-系列方法的封装

在我的项目开发中,咱们时常会遇到判断某个变量是否为一个有效值,或者依据变量的类型,依据不同的类型进行不同的操作的状况。

比方最常见的,判断一个变量是否为 Truthy 值(什么是 Truthy 值):

if (value !== null && value !== undefined) {// 搞事件}

咋看之下,也就短短两个语句,但这个事件须要进行 10 次、100 次的时候,或者你会开始想到封装:

function isDefined(value) {return value !== undefined && value !== null}

既然有了这个想法,何不一干到底,咱们就间接来封装一个本人的 is 办法库。

下列的办法,将应用规范的 TypeScript 编写,你将会看到:泛型、类型谓词 is

一些惯例的 is 办法

通过类型谓词 is 能够在 TypeScript 收窄类型,帮忙更好的类型推断,这里不开展。

判断 TruthyFalsy

// 能够思考一下 value !== undefined 和 typeof value !== 'undefined' 有什么区别?// null 呢?function isDefined<T = unknown>(value: T | undefined | null): value is T {return value !== undefined && value !== null}

function isNull(value: unknown): value is null | undefined {return value === undefined || value === null}

判断其余根本类型(除了 nullundefined):

function isNumber(value: unknown): value is number {return typeof value === 'number'}

// 发问:NaN 是不是一个根本类型呢?function isNaN(value: unknown): value is number {return Number.isNaN(value)
}

function isString(value: unknown): value is string {return typeof value === 'string'}

function isBoolean(value: unknown): value is boolean {return typeof value === 'boolean'}

// 严格判断 true
function isTrue(value: unknown): value is true {return value === true}

// 严格判断 false
function isFalse(value: unknown): value is false {return value === false}

// 别忘了 Symbol
function isSymbol(value: unknown): value is symbol {return typeof value === 'symbol'}

// 还有一个根本类型,它是谁呢?

除开根本类型后,接下来就是一些常见对象类型的判断的,在这个之前能够先思考一个问题:

typeof object === 'object' 能不能无效的判断一个变量是否为对象呢?

从狭义上来讲,只有这个成立,那该变量的确是一个对象,但这往往不是咱们所须要和冀望的,因为这样并不能辨别数组 [] 和 对象 {} 的区别,包含一些其余对象如 Date

所以咱们要借助一个 大家都晓得的 绕一点的形式来判断:Object.prototype.toString,这里咱们就间接上了,不晓得具体原理的请自行搜寻。

常见对象的判断:

// 存一下,缩小对象属性的读取
const toString = Object.prototype.toString

function is(value: unknown, type: string) {return toString.call(value) === `[object ${type}]`
}

// 这里能够思考对象类型的收窄,用 Record<string, any> 是否适合?function isObject<T extends Record<string, any> = Record<string, any>>(value: unknown): value is T {return is(value, 'Object')
}

// 数组能够应用原生的办法取得更高的效率
function isArray(value: unknown): value is any[] {return Array.isArray(value)
}

// 插播一个 function
function isFunction(value: unknown): value is (...any: any[]) => any {return typeof value === 'function'}

// 补充下面被忘记的 BigInt 根本类型
function isBigInt(value: unknown): value is bigint {return typeof value === 'bigint'}

// 这里如果想要同时反对 PromiseLike 的类型收窄的话要怎么写呢?function isPromise(value: unknown): value is Promise<any> {
  return (
    !!value &&
    typeof (value as any).then === 'function' &&
    typeof (value as any).catch === 'function'
  )
}

function isSet(value: unknown): value is Set<any> {return is(value, 'Set')
}

function isMap(value: unknown): value is Map<any, any> {return is(value, 'Map')
}

function isDate(value: unknown): value is Date {return is(value, 'Date')
}

function isRegExp(value: unknown): value is RegExp {return is(value, 'RegExp')
}

留神到这里独自封装了一个 is 办法,这个办法是能够进行任意的拓展的,比方想判断一些自定义类的时候,能够基于该 is 再封装(下面的办法都是这个准则):

function isMyClass(value: unknown): value is MyClass {return is(value, 'MyClass')
}

一些不太惯例的 is 办法

除了一些类型的判断,咱们时常会有呈现像是判断该变量是否为一个 Empty 值:

什么是 Empty 指的,惯例一点来讲就是包含:空数组、空字符串、空 Map、空 Set、空对象 {}

function isEmpty(value: unknown) {if (Array.isArray(value) || typeof value === 'string') {return value.length === 0}

  if (value instanceof Map || value instanceof Set) {return value.size === 0}

  if (isObject(value)) {return Object.keys(value).length === 0
  }

  return false
}

还有一个比拟常见的场景是,判断某个变量是否是否个对象的键值,咱们能够借助 Object.prototype.hasOwnProperty 来判断:

const hasOwnProperty = Object.prototype.hasOwnProperty

function has(value: Record<string, any>, key: string | symbol): key is keyof typeof value {return hasOwnProperty.call(value, key)
}

整合一下

好了,到此为止一个蕴含了根本类型和一些常见类型的 is 函数库就功败垂成了,最初附上一份整合后的残缺代码,大家能够在这个根底上做一些本人的拓展(应该没人须要纯 js 版本的吧):

const toString = Object.prototype.toString
const hasOwnProperty = Object.prototype.hasOwnProperty

export function is(value: unknown, type: string) {return toString.call(value) === `[object ${type}]`
}

export function has(value: Record<string, any>, key: string | symbol): key is keyof typeof value {return hasOwnProperty.call(value, key)
}

export function isDefined<T = unknown>(value: T | undefined | null): value is T {return value !== undefined && value !== null}

export function isNull(value: unknown): value is null | undefined {return value === undefined || value === null}

export function isNumber(value: unknown): value is number {return typeof value === 'number'}

export function isNaN(value: unknown): value is number {return Number.isNaN(value)
}

export function isString(value: unknown): value is string {return typeof value === 'string'}

export function isBoolean(value: unknown): value is boolean {return typeof value === 'boolean'}

export function isTrue(value: unknown): value is true {return value === true}

export function isFalse(value: unknown): value is false {return value === false}

export function isSymbol(value: unknown): value is symbol {return typeof value === 'symbol'}

export function isBigInt(value: unknown): value is bigint {return typeof value === 'bigint'}

export function isArray(value: unknown): value is any[] {return Array.isArray(value)
}

export function isObject<T extends Record<string, any> = Record<string, any>>(value: unknown): value is T {return is(value, 'Object')
}

export function isPromise(value: unknown): value is Promise<any> {
  return (
    !!value &&
    typeof (value as any).then === 'function' &&
    typeof (value as any).catch === 'function'
  )
}

export function isFunction(value: unknown): value is (...any: any[]) => any {return typeof value === 'function'}

export function isSet(value: unknown): value is Set<any> {return is(value, 'Set')
}

export function isMap(value: unknown): value is Map<any, any> {return is(value, 'Map')
}

export function isDate(value: unknown): value is Date {return is(value, 'Date')
}

export function isRegExp(value: unknown): value is RegExp {return is(value, 'RegExp')
}

export function isEmpty(value: unknown) {if (Array.isArray(value) || typeof value === 'string') {return value.length === 0}

  if (value instanceof Map || value instanceof Set) {return value.size === 0}

  if (isObject(value)) {return Object.keys(value).length === 0
  }

  return false
}

一些碎碎念

最近回忆了这几年的工作,发现自己封装过各种各样的工具函数,但很多都是零零散散地遍布在我的项目中。

也是出于整顿和温习的目标,想着分享一下本人写过的一些货色,于是便尝试写了这篇文章,心愿能帮忙到一些人。

退出移动版