JavaScript/TypeScript/NodeJS实用编程工具集 - @jcstdio/jc-utils模块

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 本文介绍一个 JavaScript/TypeScript/NodeJS实用编程工具集 - @jcstdio/jc-utils模块

JavaScript/TypeScript编程工具集JavaScript/TypeScript/NodeJS实用工具模块


李俊才 (jcLee95)

已入驻阿里云社区

邮箱 :291148484@163.com

模块 npm 地址https://www.npmjs.com/package/@jcstdio/jc-utils

本文地址:


目 录



1. 介绍 与 安装

1.1 介绍

这个模块是我的个人工具模块,用于记录和收集各种用于 JavaScript/TypeScript 中的编程小工具。这些小工具来源于日常的开发或者阅读源码等待不同场景下的总结,即相当于我的笔记本,也是我用于真实项目中直接拿来用的工具。这些工具都比较小,故而放在一起。也欢迎读者朋友参与维护,一起添加更多的小工具。

1.2 安装(npm、yarn、pnpm)

npm i @jcstdio/jc-utils
# or
yarn add @jcstdio/jc-utils
# or
pnpm i @jcstdio/jc-utils

2. 判断工具(isXXX)

这部分函数主要用于各种判断,都是一些最常用的判断工具。其实很多开源项目都重复实现过它们。

// 如:
import { isBoolean } from '@jcstdio/jc-utils'

第一组 isXXX 工具:常用类型

这一部分多数是类型相关的判断,但也有例外。

工具名 描述
isBoolean 判断是否是布尔值
isString 判断是否是字符串
isNumber 判断是否是数字,不包括字符串等表示的数字
isNaNNumber 判断是否是 NaN,避免原生 isNaN 函数将 undefined 等值也当成 NaN
isNotNaNNumber 是否是非 NaN 数字
isInfinity 判断是否是无穷大值 Infinity
isNotInfinityNumber 判断是否不是 Infinity 的数字
isNotNaNOrInfinityNumber 判断是否不是 NaN 且不是 Infinity 的数字
isOddNumber 判断一个数字是否是奇数
isEvenNumber 判断一个数字是否是偶数数
isInt 是否是整数
isObject 是否是普通对象
isSymbol 判断是否是一个 symbol 类型值
isArray 是否是数组
isInt8Array 是否是 Int8Array 实例
isUint8Array 是否是 Uint8Array 实例
isUint8ClampedArray 是否是 Uint8ClampedArray 实例
isInt16Array 是否是 Int16Array 实例
isUint16Array 是否是 Uint16Array 实例
isInt32Array 是否是 Int32Array 实例
isUint32Array 是否是 Uint32Array 实例
isBigInt64Array 是否是 BigInt64Array 实例
isBigUnit64Array 是否是 BigUnit64Array 实例
isFloat32Array 是否是 Float32Array 实例
isFloat64Array 是否是 Float64Array 实例
isArrayBuffer 是否是 ArrayBuffer 实例
isDataView 是否是 DataView 实例
isFunction 是否是函数
isMap 是否是映射(ES6 Map)
isWeakMap 是否是 WeakMap 实例
isSet 是否是集合(ES6 Set)
isWeakSet 是否是 WeakSet 实例
isWeakRef 是否是 WeakRef 实例
isRegExp 是否是正则
isDate 是否是 Date 实例
isError 是否是 Error 实例
isPrimitiver 是否是原始值
isPromise 是否是 Promise
isDefined 是否非 undefined
isUndefined 是否是 undefined
isNull 是否是 null
isNullAndUndefined isUndefined 且 isNull 返回 true
isNullOrUndefined isNull 或 isUndefined 返回 true

第二组 isXXX 工具:字符串匹配

这一组 isXXX 工具主要是通过正则表达式来检测是否是某个格式的字符串。这些工具是笔者个人觉得可能用得更多的,你也可以通过 12. 正则与正则工具 中现成的正则表达式变量来快速封装(使用正则对象的 test 方法)。

工具名 描述
isChinese 匹配是否为中文字符
isEnglish 匹配是否为英文字母
isUrl 匹配是否为URL地址
isPhone 匹配是否为手机号码字符串
isEmail 匹配是否为邮箱地址
isIdCard 匹配是否为身份证号码字符串
isNumberStr 匹配数字字符串
isDecimalStr 匹配小数字符串
isIntegerStr 匹配整数字符串
isTime 匹配时间字符串 格式:hh:mm:ss
isPostalCode 匹配邮政编码字符串
isHtmlTag 匹配HTML标签字符串
isQQ 匹配QQ号码字符串
isWechat 匹配微信号字符串
isLicensePlate 匹配车牌号码字符串
isHexColor 匹配16进制颜色值字符串
isChineseName 匹配中文姓名字符串
isBankCard 匹配银行卡号字符串
isNumberAndLetter 匹配数字和字母组合字符串
isChineseEnglishNumberUnderline 匹配中文、英文、数字和下划线组成的字符串
isChineseEnglishNumberSpace 匹配中文、英文、数字和空格组成的字符串
isChineseEnglishNumberDash 匹配中文、英文、数字和中划线组成的字符串
isChineseEnglishNumberSlash 匹配中文、英文、数字和斜杠组成的字符串
isChineseEnglishNumberColon 匹配中文、英文、数字和冒号组成的字符串
isChineseEnglishNumberQuestion 匹配中文、英文、数字和问号组成的字符串
isChineseEnglishNumberExclamation 匹配中文、英文、数字和感叹号组成的字符串

第三组 isXXX 工具:运行时/运行环境

工具名 描述
isBrowser 判断是否是浏览器
isServer 服务端环境,相比于 isNode,该工具不会检查 process.versions.node
isNode 判断是否是 NodeJS
isElectron 判断是否是 election
isBrowserAndNode 同时满足 浏览器 和 NodeJS 运行环境,如 electron
isNotBrowser 不是浏览器环境
isNotServer 不是服务端环境

3. 事件发布器 - Event.EventEmitter

这个对象是 发布订阅 模式在 事件(event)中的应用,它与 NodeJS 环境下的 EventEmitter 接口相近。如想了解相关原理,请参考我的博文:《发布订阅模式原理及其应用(多种语言实现)》https://blog.csdn.net/qq_28550263/article/details/129930814。有了这个对象,你也可以在 浏览器 环境中直接玩 EventEmitter

import { EventEmitter } from '@jcstdio/jc-utils'

3.1 订阅事件

3.1.1 理解事件侦听器

我在博文《发布订阅模式原理及其应用(多种语言实现)》https://blog.csdn.net/qq_28550263/article/details/129930814中提到这样一个观点:当消息发布时,每一个订阅者可能希望以特定的形式接收订阅的消息。EventEmitter 中的的 侦听器 本质上是一个函数,其在 发布-订阅 模式中起到的作用就是上面所说的以特定的方式将消息发布给 订阅者(观察者)。例如,当我们在 一个前端响应式架构中,通过编写 侦听器 函数用于改写 视图(View) 上的数据,一旦数据发生改变,调用 emit 方法 发布 改变的发生,触发这些用于更新视图的 侦听器 (可以理解为订阅者、监听者、观察者)函数执行,这就实现了 数据变化,视图随之动态改变

3.2.1 订阅事件(侦听事件)的相关方法

在 EventEmitter 实例上,有以下方法可以用于订阅事件。:

  • addListener方法:为给定的事件添加一个新的侦听器。

一个事件可以通过调用该方法添加多个监听器到该事件的监听器数组。从 发布-订阅模式中理解,可以举一个这样的例子:

  • 售楼中心登记购买意向信息到登记表中,以到确定何时开始销售楼盘的那一刻把这个消息通知给这些意向用户。但是毕竟意向用户不止一个,我们需要一个小本子对这些用户的信息安装它们订阅消息的先后进行依次登记。这个小本子就是该 “购楼意向事件” 的 事件侦听器数组。每当有一个新的客户过来登记意向时,只需要调用 addListener 方法向该数组里新增一个侦听器函数,就实现了消息的订阅。
  • on方法:addListener 方法的别名;
  • once方法:添加一个一次性的事件,该事件触发一次后将自动被移除。

发布-订阅 模式中理解,可以举一个这样的例子:

  • 有一个人去售楼中心咨询什么时候开始售楼,但是这回售楼中心的人也不确定。这里,售楼中心是 发布者,该购楼咨询者为订阅者。由于对于该咨询购楼者来说,这个消息一旦直到就没有必要反复通知了,因此他希望只接收一次消息就不再需要订阅了。因此其在的订阅时后,使用的是 once 方法,表示消息一旦发布则不再接收下一次发布。
  • prependListener方法:为给定的事件添加一个新的侦听器,但是与 addListener 的不同在于,addListener 是从该事件的监听器数组的最后插入新的监听器,而 prependListener 将从该数组的最前面添加。

发布-订阅模式中理解,可以举一个这样的例子:

  • 售楼中心已经登记了大量有购房意向的客户,但是这会儿突然来了一个叫做某云的浙江富商。考虑到不是所有意向用户最后都一定有足够的财力付款,但是售楼中心坚信这名富商却一定有这个能力,因此欲将其作为 Very Important Person(VIP)以在执行emit 方法时,将消息优先发布给该富商。这时就需要将该富商登记在最前面,售楼中心于是调用了prependListener方法。
  • prependOnceListener方法:为给定的事件从其监听器实则前方添加一个新的一次性侦听器。

3.2 退订事件

退定事件是订阅事件的 逆逻辑,退订事件不是删除事件。

再从 发布-订阅 模式上举个例子:

  • 当富商某云(订阅者之一)看准了另外一处楼盘,不打算在接收这个楼盘的开盘消息时,对于他来说就需要退订消息。不过从销售中心的角度(发布者)看,即使一个订阅者不再继续订阅,那也还有其它的购买意向客户等待着发布消息,因此事件仍然得继续,即登记客户的本本不会因为一个客户的退订而进行销毁。

EventEmitter对象中,以下方法用于退订:

  • removeListener 方法:移除指定事件的指定监听器。也就相当于一个订阅者的退订。
  • off 方法:removeListener 方法的别名。
  • removeAllListeners 方法: 移除指定事件的所有监听器。

3.3 发布事件

一旦发布某个事件,将逐个执行该事件的侦听器数组中的所有侦听器函数。使用 emit 方法可以发布指定的事件。

3.4 EventEmitter 的类型签名

EventEmitter 对象的类型签名如下:

export declare class EventEmitter {
    private _events;
    private _maxListeners;
    constructor();
    /**
     * 将 `listener` 函数添加到名为 `event` 的事件的 listeners 数组的末尾。
     * - 不检查是否已经添加了 `listener`。
     * - 多次调用传递 `event` 和  `listener` 的相同组合将导致多次添加和调用 `listener` 。
     * @param event 时间名
     * @param listener 订阅该事件的监听器函数
     * @returns 返回对 `EventEmitter`的引用,以便可以链接调用。
     * @since v0.0.19 重大变化:当添加新的监听器到监听器数组时,将自己发出 `newListener` 事件
     */
    addListener(event: string | symbol, listener: (...args: any[]) => void): this;
    /**
     * 添加 listener, addListener 的同名方法
     * @param event 事件名
     * @param listener 订阅该事件的监听器函数
     */
    on(event: string | symbol, listener: (...args: any[]) => void): this;
    /**
     * 为名为 event 的事件添加 一次性 的 listener 函数。
     * 下次 event 被触发时,该 listener 被移除,然后被调用。
     * @param event 事件名字符串
     * @param listener 事件回调函数
     * @returns 返回对 `EventEmitter`的引用,以便可以链接调用。
     */
    once(event: string | symbol, listener: Function): this;
    /**
     * 从名为 event 的事件的侦听器数组中移除指定的 listener。
     * @param event 事件名
     * @param listener 事件监听器函数(事件的订阅者函数)
     * @returns 返回对 `EventEmitter`的引用,以便可以链接调用。
     */
    removeListener(event: string | symbol, listener: (...args: any[]) => void): this;
    /**
     * 从名为 event 的事件的侦听器数组中移除指定的 listener。 removeListener 的同名方法
     * @param event
     * @param listener
     */
    off(event: string | symbol, listener: (...args: any[]) => void): this;
    /**
     * 移除所有的 Listener
     * @param event 事件名
     * @returns 返回对 `EventEmitter`的引用,以便可以链接调用。
     */
    removeAllListeners(event?: string | symbol): this;
    /**
     * 获取所有事件名
     * @returns 事件名数组
     * @since v0.0.19 新增该方法
     */
    eventNames(): Array<string | symbol>;
    /**
     * 设定最大 Listener 数量
     * @param n 最大 Listener 数量
     */
    set maxListeners(n: number);
    /**
     * 获取最大 Listener 数量
     * - 默认情况下,任何单个事件最多可以注册10个侦听器。可以使用emitter.setMaxListeners(n)方法为单个EventEmitter实例更改此限制。
     */
    get maxListeners(): number;
    /**
     * 设定最大 Listener 数量
     * - setter maxListeners 的方法形式
     * @param n 最大 Listener 数量
     * @return 返回对 `EventEmitter`的引用,以便可以链接调用。
     */
    setMaxListeners(n: number): this;
    /**
     * 获取最大 Listener 数量
     * - 默认情况下,任何单个事件最多可以注册10个侦听器。可以使用emitter.setMaxListeners(n)方法为单个EventEmitter实例更改此限制。
     * - getter maxListeners 的方法形式
     */
    getMaxListeners(): number;
    /**
     * 返回名为 `event` 的事件的侦听器数组的副本。
     *
     * @param event 一个表示事件(key)的字符串
     * @returns 事件回调
     */
    listeners(event: string | symbol): Function[];
    /**
     * 返回名为 `eventName` 的事件的侦听器数组的副本,包括任何包装(如由 `.once()` 创建的包装)。
     * @param event
     * @returns
     */
    rawListeners(event: string | symbol): Function[];
    /**
     * 事件发生器
     *
     * - 若指定事件不存在,则返回 flase
     * - 反之,则执行指定事件的所有回调,并返回true
     *
     * @param event 事件名字符串(key)
     * @param args 事件回调的参数
     * @returns 表示事件是否存在的布尔值
     */
    emit(event: string | symbol, ...args: any[]): boolean;
    /**
     * 返回侦听名为eventName的事件的侦听器数量。如果提供了listener,它将返回在事件的侦听器列表中找到该侦听器的次数。
     *
     * 当指定的事件不存在时返回值为0
     * @param event 监听的事件名
     * @param listener 事件处理函数
     * @returns 指定事件的现有监听器的数量
     * @since v0.0.19 允许指定特定的监听器函数(listener)进行统计
     * ```ts
     * import { EventEmitter } from '@jcstdio/jc-utils'
     *
     * const myEmitter = new EventEmitter();
     * myEmitter.on('event', () => {});
     * myEmitter.on('event', () => {});
     * console.log(myEmitter.listenerCount('event'));
     * // Prints: 2
     * ```
     */
    listenerCount(event: string | symbol, listener?: Function): number;
    /**
     * 将 `listener` 函数添加到名为 `event` 的事件的 listeners 数组的开头。
     * - 不检查是否已经添加了 `listener`。
     * - 多次调用传递“事件名称”和 `listener` 的相同组合将导致多次添加和调用 `listener`。
     * @param event 事件名
     * @param listener 事件回调函数
     * @returns 返回对 `EventEmitter`的引用,以便可以链接调用。
     */
    prependListener(event: string | symbol, listener: Function): this;
    /**
     * 将名为 `event` 的事件的 **一次性** 的 `listener` 函数添加到 listeners 数组的`_beginning_ ` 中。
     * 下次 `event` 事件被触发时,该 监听器 被移除,然后被调用。
     * @param event
     * @param listener 监听器回调函数
     */
    prependOnceListener(event: string | symbol, listener: Function): this;
    /**
     * 此符号应用于安装一个监听器,仅用于监控`'error'`事件。在调用常规的`'error'`侦听器之前,调用使用此符号安装的侦听器。
     *
     * 使用此符号安装侦听器不会改变发出 `'error'` 事件后的行为,因此,如果没有安装常规的 `'error'` 侦听器,进程仍会崩溃。
     */
    static readonly errorMonitor: unique symbol;
    static readonly captureRejectionSymbol: unique symbol;
    /**
     * 设置或获取所有发射器的默认 captureRejection 值。
     */
    static captureRejections: boolean;
    /**
     * @since v0.0.19 新增 defaultMaxListeners,表示默认的最大监听器数量
     */
    static defaultMaxListeners: number;
}

4. 状态管理器 - Event.StateManager

这是我尝试使用发布订阅模式实现状态管理时实现的一个对象。请参考下面的例子:

import { StateManager } from '@jcstdio/jc-utils'
const stateManager = new StateManager<{
    value:number
}>({ value: 0, });
const sideEffec1 = (state: { value: any; }) => {
    console.log('我是 sideEffec1 ,状态被更新,当前状态值为:', state.value);
}
const sideEffec2 = () => {
  console.log('我是 sideEffec2 ,执行顺序为执行 “subscribe” 方法的顺序。')
}
// 设置状态更新后的回调,可以采用链式调用
stateManager
  .subscribe(sideEffec1)
  .subscribe(sideEffec2)
// 更新你的状态
console.log('------- 第一次改变状态 -------');
stateManager.state = { value: 1 };
console.log('\n------- 第二次改变状态 -------');
stateManager
  .unsubscribe(sideEffec2)  // 取消状态发生改变时订阅的 sideEffec2。
  .state = { value: 2 };    // 指挥执行没有被取消的订阅

outputis:

------- 第一次改变状态 -------
我是 sideEffec1 ,状态被更新,当前状态值为: 1
我是 sideEffec2 ,执行顺序为执行 “subscribe” 方法的顺序。
------- 第二次改变状态 -------
我是 sideEffec1 ,状态被更新,当前状态值为: 2

5. 动画工具

这个受启发于 JQuery 中提供的同名函数。早期我用 JQuery做手风琴效果时,用过这样的函数:

// JQuery
$(".btn1").click(function(){
  $("p").slideUp();
});
$("button").click(function(){
$("p").slideDown();
});

我当然不会在 vue 之类的项目中安装一个 JQuery 然后掉用 JQuery 的 slideUp、slideDown 等方法。于是自己封装了以一下。

以下是在一个 Vue3 的单文件组件中使用我封装版本的 slide 函数例子:

<template>
<ul @click="$event=>callBack()" ref="ulRef">
  <li>1</li>
  <li>2</li>
  <li>3</li>
  <li>4</li>
  <li>5</li>
</ul>
</template>
<script setup lang="ts">
import { getCurrentInstance, ref } from 'vue';
import type { ComponentInternalInstance } from 'vue;
import { slideUp, slideDown, slideToggle } from '@jcstdio/jc-utils'
const that = getCurrentInstance() as ComponentInternalInstance;
let callBack: () => void;
onMounted(()=>{
  const ulRef; = that.refs.ulRef as HTMLElement;
  callBack = () => {
    slideToggle(navRef);
  };
})
</script>

6. 日志器工具:Logger

这是一个轻量级的日志器,它的色彩系统基于 jc-color 模块,意味着其高亮显示可以同时在 NodeJS 终端浏览器 终端 中使用:

import { Logger } from '@jcstdio/jc-utils'
const logger = new Logger('')
logger.trace('trace')
logger.debug('debug')
logger.info('info')
logger.success('success')
logger.warn('warn')
logger.error('error')
logger.fatal('fatal')
logger.level1('level1')
logger.level2('level2')
logger.level3('level3')
logger.gradient('geadientText')

6666666666666.png

7. 配置文件工具:

7.1 ini 配置文件读取器:IniConfig

import { IniConfig } from '@jcstdio/jc-utils'

该工具的类型签名为:

declare class IniConfig {
    private _filePath;
    private _conf;
    constructor(_filePath?: string);
    get value(): Record<string, Record<string, any>>;
    load(obj: Record<string, Record<string, any>>): void;
    readSync<T>(filePath?: string): this;
    static format<U = Record<string, Record<string, any>>>(obj: U): string;
    private _farmat;
    writeSync(filePath?: string): void;
    addSection<V extends Record<string, any>>(section: string, records: V): this;
    addRecord<V>(section: string, key: string, value: V): this;
    getSection(section?: string): Record<string, any>;
    get(section?: string, key?: string): any;
    private __str__;
    print(): this;
}

7.2 env 配日志文件读取器:getEnvs

import { getEnvs } from '@jcstdio/jc-utils'

该工具的类型签名为:

declare function getEnvs<T>(envFilePath: string, regExp?: RegExp, parse?: (right: string) => T): Record<string, T>;

8. 缓存工具

8.1 cookie 工具:Cookies

import { Cookies } from '@jcstdio/jc-utils'
let cookies = new Cookies();

Cookie 对象的类型签名为:

declare class Cookies {
    /**
     * 添加新的 cookie
     * @param name cookie 名称
     * @param value cookie 值
     * @param options cookie 选项
     */
    set(name: string, value: string, options?: {
        expires?: number;
        path?: string;
    }): void;
    /**
     * 获取指定名称的 cookie
     * @param name cookie 名称
     * @returns cookie 值
     */
    get(name: string): string | undefined;
    /**
     * 删除指定名称的 cookie
     * @param name cookie 名称
     */
    remove(name: string): void;
    /**删除所有的 cookie*/
    clear(): void;
    /**
     * 返回所有 cookie 键值对构成的 Map
     * @returns cookie 键值对构成的 Map
     */
    getMap(): Map<string, string>;
    /**
     * 返回指定 cookie 是否存在
     * @param name cookie 名称
     * @returns 是否存在
     */
    has(name: string): boolean;
    /**
     * 将指定 cookie 键值对写入 localStorage
     * @param name cookie 名称
     */
    copyToLocal(name: string): void;
    /**
     * 将指定 cookie 键值对写入 localStorage,并从 cookie 中删除
     * @param name cookie 名称
     */
    moveToLocal(name: string): void;
    /**
     * 从 localStorage 读取指定键值对,改写到 cookie,并从 localStorage  中删除
     * @param name cookie 名称
     */
    moveFromLocal(name: string): void;
    /**
     * 从 localStorage 读取指定键值对,改写到 cookie
     * @param name cookie 名称
     */
    copyFromLocal(name: string): void;
}

9. 拷贝工具

提供深拷贝、浅拷贝的相关函数。请参考以下例子使用:

import { copy } from '@jcstdio/jc-utils'
// 浅拷贝
const obj1 = {a: 1, b: {c: 2}};
const shallowCopyObj1 = copy.shallowCopy(obj1);
console.log(shallowCopyObj1); // {a: 1, b: {c: 2}}
console.log(shallowCopyObj1.b === obj1.b); // true
// 使用 deepCopy 方法深拷贝 (递归复制嵌套对象实现)
const obj2 = {a: 1, b: {c: 2}};
const deepCopyObj2 = copy.deepCopy(obj2);
console.log(deepCopyObj2); // {a: 1, b: {c: 2}}
console.log(deepCopyObj2.b === obj2.b); // false
// 使用 serializeCopy 方法深拷贝 (使用JSON序列化和反序列化实现)
const obj3 = {a: 1, b: {c: 2}};
const serializeCopyObj3 = copy.serializeCopy(obj3);
console.log(serializeCopyObj3); // {a: 1, b: {c: 2}}
console.log(serializeCopyObj3.b === obj3.b); // false

10. vite 相关工具

环境变量筛选:wrapperEnv

这个函数可以帮助你加载带有给定前缀的env值。

// vite.config.ts
import { wrapperEnv } from '@jcstdio/jc-utils'
const ENV_DIR = path.join(__dirname, "envs");
export default defineConfig(({ command, mode }) => {
  const env = loadEnv(mode, ENV_DIR, "");
  const viteEnv = wrapperEnv(env);
  console.log((`[vite config] viteEnv founded: ${JSON.stringify(viteEnv)}`));
  return {
    // vite configs
  // ...
  }
})

11. 数学计算相关工具

等差数列工具:arithmeticProgression

用于构造一个等差数列数组

import { arithmeticProgression } from '@jcstdio/jc-utils'
arithmeticProgression(1,9,3);    // [ 1, 3, 5, 7, 9 ]
arithmeticProgression(9,1,3);    // [ 9, 7, 5, 3, 1 ]
arithmeticProgression(-9,-1,3);  // [ -9, -7, -5, -3, -1 ]
arithmeticProgression(-1,-9,3);  // [ -1, -3, -5, -7, -9 ]
arithmeticProgression(-1,-9,0);  // [ -1, -9 ]
arithmeticProgression(1,1,3);    // [ 1, 1, 1, 1, 1 ]

等间隔序列生成器:range

该工具受启发于 Python 中提供的 range 函数。在本模块中,range 是一系列重载的序列生成器,返回一个数组。

range 工具的类型签名如下:

declare function range(x: number): number[];
declare function range(x: [number]): number[];
declare function range(x: [number, number]): number[];
declare function range(x: [number, number, number]): number[];

例如:

import { range } from '@jcstdio/jc-utils'
range([0, 3]);    // [0,1,2]
range(3);         // 相当于 range([0, 3]),[0,1,2]
range([3]);       // 相当于 range(3),   [0,1,2]
range([4, 8, 1]); // [4, 5, 6, 7], 其中最后一个数表示间隔
range([4, 8, 2]); // [4, 6]
range([4, 8]);    // 相当于 range([4, 8, 1]),[4, 5, 6, 7]

12. 正则与正则工具

这一部分的包含各种来源于以前写过的以及各种开源项目和网页上归纳出来的正则表达式,如果由其它常用的,可以邮件给我,我继续添加。

本节列出的正则工具,可以参考如下格式进行导入:

import { regIp } from '@jcstdio/jc-utils'

12.1 网络相关正则表达式

正则实例变量名 描述
regIp 匹配IP地址
regMac 匹配MAC地址
regEmail 匹配Email地址
regMacOrIp 匹配MAC地址或IP地址
regUrl 匹配网址URL的正则表达式
regIpv6 匹配IPv6地址
regDomain 匹配域名
regPort 匹配端口号
regSubnetMask 匹配子网掩码

12.2 字符格式相关正则表达式

正则实例变量名 描述
regChinese 匹配中文字符
regDoubleByte 匹配双字节字符(包括汉字在内)
regTrim 匹配首尾空白字符的正则表达式
regSpecialChar 匹配特殊字符
regNumberAndLetter 匹配数字和字母组合
regChineseEnglishNumberUnderline 匹配中文、英文、数字和下划线
regChineseEnglishNumberSpace 匹配中文、英文、数字和空格
regChineseEnglishNumberDash 匹配中文、英文、数字和中划线
regChineseEnglishNumberSlash 匹配中文、英文、数字和斜杠
regChineseEnglishNumberColon 匹配中文、英文、数字和冒号
regChineseEnglishNumberQuestion 匹配中文、英文、数字和问号
regChineseEnglishNumberExclamation 匹配中文、英文、数字和感叹号
regChineseEnglishNumberUnderscore 匹配中文、英文、数字及下划线
regChineseEnglishNumberUnderscoreLength 匹配中文、英文、数字及下划线,且长度为2-18个字符
regChineseEnglishNumberUnderscore2 匹配由数字、26个英文字母、中文或者下划线组成的字符串
regChineseEnglishNumberUnderscoreLength2 匹配由数字、26个英文字母、中文或者下划线组成的字符串,且长度为2-18个字符
regChineseEnglishNumberUnderscoreLength3 匹配由数字、26个英文字母、中文或者下划线组成的字符串,且长度为6-18个字符
regChineseEnglishNumberUnderscoreLength4 匹配由数字、26个英文字母、中文或者下划线组成的字符串,且长度为8-16个字符
regChineseEnglishNumberUnderscoreLength5 匹配由数字、26个英文字母、中文或者下划线组成的字符串,且长度为6-16个字符
regEnglishAlphabet 匹配由26个英文字母组成的字符串
regUppercaseEnglishAlphabet 匹配由26个大写英文字母组成的字符串
regLowercaseEnglishAlphabet 匹配由26个小写英文字母组成的字符串
regAlphanumeric 匹配由数字和26个英文字母组成的字符串
regUnderscore 匹配由数字、26个英文字母或者下划线组成的字符串
regChineseEnglishNumber 匹配由数字、26个英文字母或者中文组成的字符串

12.3 标签、各种文本相关正则表达式

正则实例变量名 描述
regXmlTag 匹配XML标签
regHtmlTag 匹配HTML标记
regYaml 匹配YAML格式的文本
regJson 匹配JSON格式的文本

12.4 日期时间相关正则表达式

正则实例变量名 描述
regDate 匹配日期格式:yyyy-mm-dd
regTime 匹配时间(hh:mm:ss)
regDateTime 匹配日期时间格式:yyyy-mm-dd HH:MM:SS

12.5 数字相关正则表达式

正则实例变量名 描述
regNumber 匹配十进制数字
regNonNegativeInteger 匹配十进制非负整数(正整数 + 0)
regPositiveInteger 匹配十进制正整数
regNegativeInteger 匹配十进制负整数
regNonPositiveInteger 匹配十进制非正整数(负整数 + 0)
regInteger 匹配十进制整数
regOctalNumber 匹配表示八进制数的字符串
regDecimalNumber 匹配表示十进制数的字符串
regHexadecimalNumber 匹配表示十六进制数的字符串
regFloat 匹配浮点数
regPositiveFloat 匹配正浮点数
regNegativeFloat 匹配负浮点数
regNonNegativeFloat 匹配非负浮点数(正浮点数 + 0)
regNonPositiveFloat 匹配非正浮点数(负浮点数 + 0)

12.6 颜色相关正则表达式

正则实例变量名 描述
regHexColor 匹配16进制颜色值
regRgbColor 匹配RGB颜色值
regRgbaColor 匹配RGBA颜色值
regHslColor 匹配HSL颜色值
regHslaColor 匹配HSLA颜色值

12.7 电话号码、邮编相关正则表达式

正则实例变量名 描述
regTelephone 匹配电话号码(支持带区号和不带区号)
regMainlandPhone 匹配中国大陆手机号码(支持+86和86开头)
regMainlandTelephone 匹配中国大陆固定电话号码
regMainlandTelecomPhone 匹配中国大陆电信手机号码
regMainlandUnicomPhone 匹配中国大陆联通手机号码
regMainlandMobilePhone 匹配中国大陆移动手机号码
regPostalCode 匹配中国邮政编码
regMainlandPostalCode 匹配中国大陆邮政编码

12.8 互联网账号相关正则表达式

正则实例变量名 描述
regQq 匹配腾讯QQ号
regWechat 匹配微信号
regWeiboUid 匹配微博UID
regSinaWeibo 匹配新浪微博账号
regBaidu 匹配百度账号
regAlipay 匹配支付宝账号
regTaobao 匹配淘宝账号
regJd 匹配京东账号
regSuning 匹配苏宁账号
regVph 匹配唯品会账号
regDangdang 匹配当当账号
reg163Email 匹配网易邮箱账号
regQqEmail 匹配QQ邮箱账号
regSinaEmail 匹配新浪邮箱账号
regAliyun 匹配阿里云账号
regHuaweiCloud 匹配华为云账号
regTencentCloud 匹配腾讯云账号
regJdCloud 匹配京东云账号
regAws 匹配AWS账号
regAzure 匹配Azure账号
regTwitter 匹配Twitter账号
regFacebook 匹配Facebook账号
regLinkedIn 匹配LinkedIn账号
regGitHub 匹配GitHub账号
regGitLab 匹配GitLab账号
regBitbucket 匹配Bitbucket账号
regDockerHub 匹配Docker Hub账号
regGoogle 匹配Google账号
regMicrosoft 匹配Microsoft账号
regApple 匹配Apple账号
regAmazon 匹配Amazon账号
regNetflix 匹配Netflix账号
regHulu 匹配Hulu账号
regSpotify 匹配Spotify账号
regTwitch 匹配Twitch账号
regReddit 匹配Reddit账号
regDiscord 匹配Discord账号
regSlack 匹配Slack账号
regZoom 匹配Zoom账号
regInstagram 匹配Instagram账号
regSnapchat 匹配Snapchat账号
regPinterest 匹配Pinterest账号
regTikTok 匹配TikTok账号
regTelegram 匹配Telegram账号
regWhatsApp 匹配WhatsApp账号
regLine 匹配Line账号
regViber 匹配Viber账号
regSignal 匹配Signal账号

12.9 证件号码相关正则表达式

正则实例变量名 描述
regIdCard 匹配中国大陆身份证号码1
regMainlandIdCard 匹配中国大陆身份证号码2
regLicensePlate 匹配车牌号码
regMainlandPassport 匹配中国大陆护照号码
regMainlandLicensePlate 匹配中国大陆车牌号码(新能源+非新能源)
regMainlandIdCard2 匹配中国大陆身份证号码(18位,支持港澳台)
regMainlandHKMacaoPass 匹配中国大陆港澳居民来往内地通行证
regMainlandTaiwanPass 匹配中国大陆台湾居民来往大陆通行证
regMainlandOfficerRetireCard 匹配中国大陆军官离退休证
regMainlandSocialSecurityCard 匹配中国大陆社会保障卡
regMainlandOfficerCard 匹配中国大陆军官证
regMainlandSoldierCard 匹配中国大陆士兵证

12.10 工具函数:regTestAll

regTestAll 函数接收一组正则表达式和一个字符串。当所有的正则表达式都在该字符串上 test 成功时返回 ture,否则返回 false。该函数的类型签名为:

declare function regTestAll(regs: RegExp[], text: string, debug?: boolean): boolean;

12.10 工具函数:regTestAny

regTestAll 函数接收一组正则表达式和一个字符串。当任意一个正则表达式都在该字符串上 test 成功时返回 ture,全部test失败时返回 false。该函数的类型签名为:

declare function regTestAny(regs: RegExp[], text: string, debug?: boolean): boolean;

13. 数据容器工具

13.1 双链表容器: LinkedList 类

LinkedList 是一个使用 ListNode 类作为节点的 双链表,这两个类的类型签名为:

declare class ListNode<T> {
    value: T;
    next: ListNode<T> | null;
    prev: ListNode<T> | null;
    constructor(value: T);
}
declare class LinkedList<T> {
    private _head;
    private _tail;
    private _count;
    constructor(...params: T[]);
    /**获取头节点*/
    get head(): ListNode<T>;
    /**获取尾节点*/
    get tail(): ListNode<T>;
    /**链表的长度*/
    get length(): number;
   /**
    * 在链表尾部添加节点
    * @param value 节点的值
    */
    push(value: T): void;
    /**
   * append 的别名方法
   * @param value 节点的值
   */
    append(value: T): void;
    /**
   * 在链表头部添加节点
   * @param value 节点的值
   */
    pushLeft(value: T): void;
    /**
   * addpenLeft的别名方法
   * @param value 节点的值
   */
    appendLeft(value: T): void;
    /**
   * 删除链表尾部节点
   * @returns 被删除的节点的值
   */
    pop(): T | null;
    /**
   * 在链表头部添加节点
   * @param value 节点的值
   */
    unshift(value: T): void;
    /**
   * 清空链表
   */
    clear(): void;
    /**
   * 删除指定节点
   * @param node 要删除的节点
   * @returns 被删除的节点的值
   */
    remove(node: ListNode<T>): T | null;
    /**
   * 删除指定位置的节点
   * @param index 要删除的节点的位置
   * @returns 被删除的节点的值
   */
    drop(index: number): T | null;
    /**
   * 对该链表进行切片,返回新的 LinkedList
   * @param start 切片的起始元素索引(包含)
   * @param end 切片的结束元素索引(不包含)
   */
    slice(start: number, end: number): LinkedList<T>;
    /**
   * 如果LinkedList元素都为 number 时,用于与另外一个 LinkedList 元素对应相加,得到新的LinkedList返回。
   * 如果 LinkedList 元素都为 string 时,则返回对应字符串元素拼接组成的新LinkedList
   * @param list 要相加或拼接对应元素的 LinkedList
   * @returns 相加或拼接对应元素后的新 LinkedList
   */
    add(list: LinkedList<T>): LinkedList<T>;
    /**
   * 获取指定位置的节点的值
   * @param index 节点的位置
   * @returns 节点的值
   */
    get(index: number): T | null;
    /**
   * 判断链表中是否包含指定的值
   * @param value 要查找的值
   * @returns 是否包含指定的值
   */
    includes(value: T): boolean;
    /**
   * includes 的别名方法
   * @param value 要查找的值
   * @returns 是否包含指定的值
   */
    contains(value: T): boolean;
    /**
   * 过滤链表中的节点
   * @param callback 过滤函数
   * @returns 过滤后的链表
   */
    filter(callback: (value: T) => boolean): LinkedList<T>;
    /**
   * 映射链表中的节点
   * @param callback 映射函数
   * @returns 映射后的链表
   */
    map<U>(callback: (value: T) => U): LinkedList<U>;
    private _quickSort;
    /**
   * 对链表进行快速排序
   */
    sort(): void;
    /**
   * 对链表元素进行反转
   */
    reverse(): void;
}

List 类

基于该 双端链表封装了一个 列表类 List。其类型签名如下:

declare class List<T> {
    private _list;
    constructor(...params: T[]);
    get value(): LinkedList<T>;
    set value(list: LinkedList<T>);
   /**
     * 在列表尾部添加元素
     * @param value 元素的值
     */
    push(value: T): void;
    /**
     * 在内部,如果LinkedList元素都为 number 时,用于与另外一个 LinkedList 元素对应相加,得到新的LinkedList返回。 如果 LinkedList 元素都为 string 时,则返回对应字符串元素拼接组成的新LinkedList
     */
    add(value: LinkedList<T>): void;
    /**
     * append的别名方法
     * @param value 元素的值
     */
    append(value: T): void;
    /**
     * 在列表头部添加元素
     * @param value 元素的值
     */
    unshift(value: T): void;
    /**
     * addpenLeft的别名方法
     * @param value 元素的值
     */
    appendLeft(value: T): void;
    /**
     * 删除列表尾部元素
     * @returns 被删除的元素的值
     */
    pop(): T | null;
    /**
     * 清空列表
     */
    clear(): void;
    /**
     * 删除指定位置的元素
     * @param index 要删除的元素的位置
     * @returns 被删除的元素的值
     */
    remove(index: number): T | null;
    /**
     * 获取指定位置的元素的值
     * @param index 元素的位置
     * @returns 元素的值
     */
    get(index: number): T | null;
    /**
     * 判断列表中是否包含指定的值
     * @param value 要查找的值
     * @returns 是否包含指定的值
     */
    includes(value: T): boolean;
    /**
     * contains的别名方法
     * @param value 要查找的值
     * @returns 是否包含指定的值
     */
    contains(value: T): boolean;
    /**
     * 过滤列表中的元素
     * @param callback 过滤函数
     * @returns 过滤后的列表
     */
    filter(callback: (value: T) => boolean): List<T>;
    /**
     * 映射列表中的元素
     * @param callback 映射函数
     * @returns 映射后的列表
     */
    map<U>(callback: (value: T) => U): List<U>;
    /**
     * 获取指定范围内的元素组成的新列表
     * @param start 起始位置(包含)
     * @param end 结束位置(不包含)
     * @returns 新列表
     */
    slice(start: number, end: number): List<T>;
    /**
     * 纯基于整数位置的索引,用于按位置选择。
     * @param index 元素的位置
     * @returns 元素的值
     */
    iloc(index: number): T | null;
    /**
     * 所有元素为number时,用于求和,所有元素为string时用于字符串拼接
     * @returns 求和或字符串拼接的结果
     */
    sum(): number | string;
    /**
     * 所有元素为number时返回最小值
     * @returns 最小值
     */
    min(): number | null;
    /**
     * 所有元素为number时返回最大值
     * @returns 最大值
     */
    max(): number | null;
    /**
     * 所有元素为number时返回平均值
     * @returns 平均值
     */
    mean(): number | null;
    /**
     * 所有元素为number时返回样本标准差
     * @returns 样本标准差
     */
    std(): number | null;
    /**
     * 所有元素为number时返回无偏方差
     * @returns 无偏方差
     */
    var(): number | null;
    /**
     * 所有元素为number时返回所有该LinkedList元素绝对值构成的新LinkedList
     * @returns 所有元素绝对值构成的新LinkedList
     */
    abs(): List<number>;
    /**
     * 就地排序
     */
    sort(): void;
    /**
     * 列表的长度
     */
    get length(): number;
    /**
     * 列表的长度
     * - length 的同名 Getter.
     */
    get size(): number;
    /**
     * 实现该类的迭代器
     */
    [Symbol.iterator](): Iterator<T>;
}

13.2 一个受 pandas、danfojs 启发的序列:Series 类

declare class Series {
    private _data;
    private _index;
    private columns;
    private dtypes;
    constructor(data: any[], options?: {
        columns?: any[];
        dtypes?: any[];
        index?: any[];
    });
    get shape(): number[];
    get size(): number;
    get data(): any[];
    /**返回序列和其他元素的加法(二元运算符add)*/
    add(other: Series, options?: any): Series;
    /**返回 Series 和其他元素的减法(二元运算符sub)*/
    sub(other: Series, options?: any): Series;
    /**返回 Series 和其他元素的乘法运算(二元运算符mul)*/
    mul(other: Series, options?: any): Series;
    /**返回 Series 和其他元素的浮点除法(二元运算符truediv)*/
    div(other: Series, options?: any): Series;
    /**返回 Series 和其他元素的模(二元运算符mod)*/
    mod(other: Series, options?: any): Series;
    /**返回级数和其他元素的指数幂(二元运算符pow)*/
    pow(other: Series, options?: any): Series;
    /**舍入 Series 中的浮点值*/
    round(dp: number, options?: any): Series;
    /**检查 Series 中的所有值是否都小于某个值*/
    lt(other: any, options?: any): boolean;
    /**返回 Series 和其他元素的大于(二元运算符gt)*/
    gt(other: Series, options?: any): Series;
    /**按元素返回小于或等于of的 Series 和other(二元运算符le)*/
    le(other: Series, options?: any): Series;
    /**检查 Series 中的所有值是否都大于或等于某个值*/
    ge(other: any, options?: any): boolean;
    /**返回不等于系列和其他元素的值(二元运算符ne)*/
    ne(other: Series, options?: any): Series;
    /**返回等于系列和其他元素的值(二元运算符eq)*/
    eq(other: Series, options?: any): Series;
    /**对Series中的每个值调用函数*/
    apply(callable: Function, options?: any): Series;
    /**将Series的值映射到函数或对象*/
    map(callable: Function): Series;
    /**返回删除了缺失值的新Series*/
    dropNa(options?: any): Series;
    /**使用指定的方法填充NaN值*/
    fillNa(options?: any): Series;
    /**检测缺失值*/
    isNa(): boolean;
    /**替换值*/
    replace(oldValue: any, newValue: any, options?: any): Series;
    /**返回Series和其他Series之间的逻辑或*/
    or(other: Series): Series;
    /**返回Series和其他Series之间的逻辑与*/
    and(other: Series): Series;
    /**返回对Series值进行排序的整数索引*/
    argSort(options?: any): any[];
    /**返回 Series 中最小值的位置*/
    argMin(): number;
    /**返回 Series 中最大值的位置*/
    argMax(options?: any): number;
    /**按值排序*/
    sortValues(options?: any): Series;
    /**
    将系列打印到控制台。
    注意:这个方法的实现还有些问题,待之后想办法改改。
    */
    print(...params: any[]): void;
}

14.杂项

14.1 对应于 Omit 类型工具 的 omit 函数

TypeScript 中提供了一个内置的类型工具 Omit

(这里推荐阅读我的另外一篇博客《小结:近五十个常用 TypeScript类型工具 的声明、描述、用法示例》

type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

如:

interface User {
  name: string;
  age: number;
  email: string;
}
type UserWithoutEmail = Omit<User, 'email'>;

其中的 UserWithoutEmail 相当于:

{
  name: string;
  age: number;
}

也就是当我们从一个类型构造另外一个类型时,排除了某个属性。在 @jcstdio/jc-utils 模块中提供了一个对应于该类型工具的 omit 函数可以直接拿来用.该工具的类型签名如下:

declare function omit<T, K extends keyof T>(obj: T, key: K): Omit<T, K>;

14.2 空操作:noop、pass

这两个函数的类型签名为:

declare const noop: (...params: any) => void;
declare const pass: (param?: string) => void;

15. 字符串Base64 编解码工具

这部分提供 strEncodeBase64strBase64Decode 这两个工具,分别用于字符串的Base64编码和还原。它们的类型签名如下:

since v0.0.17

  • strEncodeBase64
/**
 * 对一个字符串进行 Base64 编码
 * @param str 一个字符串
 * @returns 进行 Base64 编码后的字符串
 */
export declare function strEncodeBase64(str: string): string;
  • strBase64Decode
/**
 * 将一个使用 Base64 编码后的字符串还原为原字符串
 * @param str 一个字符串
 * @returns Base64 解码 后的字符串
 */
export declare function strBase64Decode(str: string): string;

16. ID 管理器

16.1 ID 管理简介

在日常开发时,我们往往有这样的需求,就是自动地为某个事物分配一个唯一的标识符(ID)。 ID 管理器是一个 统一存储分散使用唯一标识值管理器。使用此管理器,你可以为多个不同事物轻松地分配 ID —— 只需要你指定不同地事物名称,就可以让一个事物不断地申请到不重复地标识字符串。

16.2 定义事物

所谓 事物 只的是任何需要使用 ID 来标识其不同个体的类别。比如一个班上的学生可以看成一个事物,学生的学号就可以视作其ID来进行管理。而定义事物地目的是为了区分这些事物,比如在一个学校中我们不仅仅需要区别学生而为它们分配学号,还需要使用职工编号来区分教职员工。

addThing 函数用于定义事物,其类型签名如下:

export declare function addThing(thingName: string, begin?: number): boolean;
  • 它接受一个字符串参数(thingName)用于标识该事物。例如:
import { addThing } from '@jcstdio/jc-utils/id'
addThing('student'); // 添加了 student 作为事物,也就是相当于新建了一个 student 的分类
addThing('staff'); // 添加了 staff 作为事物,也就是相当于新建了一个 staff 的分类
  • 另外一个数字参数(begin)用来指定内部起始的计数值,这个计数值将随着申请的 ID 数量增加而增加,默认情况下从0开始。

注意:在任何模块中使用 addThing 函数定义的事物是不能重复的。

16.3 申请 ID

假设有一个新的同学到学校进行报道时,为了让学号不重复,我们需要去查询当前的学号位置,然后为该新生登记一个新的学号,并返回告诉该学生。你可以使用 getNewItemId 函数申请一个新的 ID。该函数的类型签名为:

export declare function getNewItemId(thingName: string, idType?: IDType): string | number;

比如,为学生的分类申请一个新的 ID 用作一个新生的学号:

import { getNewItemId } from '@jcstdio/jc-utils/id'
const id = getNewItemId('student');

16.4 返回ID类型

declare type IDType = 'number' | 'string' | 'simple' | 'baseb4';

默认情况下,返回的是一个经过 Base64编码 后的 ID 字符串。

16.5 持久化和初始化

  • 使用 save 方法可以对当前管理器进行持久化。
  • 使用 load 方法可以进行加载先前持久化的存储。
    它们的类型签名如下:
export declare function load(from?: string): Promise<boolean>;
/**
 * 保存到本地存储
 * @param path — 如果指定了此参数,且在服务端环境,则会保存到以该参数为路径的位置而非 localstorage,否则保存到localstorage
 * - 你可以通过 setStorageKey 方法设置存储键
 */
export declare function save(path?: string): Promise<boolean>;
/**
 * 新建事物
 * @param thingName 事物名
 * @param begin id 计数起始
 * @returns 是否新建成功
 */

16.4 其它 API

你可以参考以下类型签名尝试其它API:

declare type IDType = 'number' | 'string' | 'simple' | 'baseb4';
/**获取 LOCAL STORAGE 键 */
export declare function getStorageKey(): string;
/**
 * 设置 LOCAL STORAGE 键
 * @param key 存储键字符串
 */
export declare function setStorageKey(key: string): string;
/**
 * 从本地存储中加载
 * @param {undefined|string} from 如果指定了此参数且在服务端环境,则从该参数表示的路径加载数据而不是 localstorage
 */
export declare function load(from?: string): Promise<boolean>;
/**
 * 保存到本地存储
 * @param path — 如果指定了此参数,且在服务端环境,则会保存到以该参数为路径的位置而非 localstorage,否则保存到localstorage
 * - 你可以通过 setStorageKey 方法设置存储键
 */
export declare function save(path?: string): Promise<boolean>;
/**
 * 新建事物
 * @param thingName 事物名
 * @param begin id 计数起始
 * @returns 是否新建成功
 */
export declare function addThing(thingName: string, begin?: number): boolean;
/**
 * 删除事物
 * @param thingName 事物名
 * @returns 删除成功则返回 true,反之返回 false
 */
export declare function dropThing(thingName: string): boolean;
/**
 * 返回事物是否存在
 * @param thingName 事物名
 * @since v0.0.19
 */
export declare function hasThing(thingName: string): boolean;
/**
 * 指定事件是否已有指定ID
 * @param {string} thingName 事件名
 * @param {number | string} id ID值
 * @param { IDType }idType ID类型
 * @returns { boolean } 如果有,则返回 true,否则返回 false
 */
export declare function hasID(thingName: string, id: number | string, idType?: IDType, raiseWarn?: boolean): boolean;
/**
 * 为指定事物申请一个新 ID
 * @param thingName 事物名
 * @returns id 字符串
 */
export declare function getNewItemId(thingName: string, idType?: IDType): string | number;
/**
 * 获取某个事物当前的ID
 * @param thingName 事物名
 * @returns 最后给出的一个 id
 */
export declare function getCurrentId(thingName: string, idType?: IDType): string | number;
/**
 * 获取某个事物当前已经给出的 id 数量
 * @param thingName 事物名
 * @returns 某个事物当前已经给出的 id 数量
 */
export declare function countIds(thingName: string): number;

17. 代理与响应式数据工具

17.1 常用代理

1. Array 的代理创建器

createArrayProxy 函数用于创建 Array 的代理,并且功能比原 Array 更加强大。比如:

  1. 可以对代理后的数组进行负数索引
  2. 删除指定索引处的元素不留下空位
  • 既可以使用 delete 关键字,也可以使用扩展的 drop 方法。不过需要指出的是,这个 drop 方法是仅仅存在于代理对象上的。

createArrayProxy 工具的类型签名如下:

/**
 * 为 Array 创建代理
 * - 可以进行负数索引
 * - 删除指定索引处的元素不留下空位
 * @param target 被代理的目标对象
 * @returns 代理后的对象
 */
declare function createArrayProxy<T>(target: Array<T>): ArrayProxy<T>;

例如:

import { createArrayProxy } from '@jcstdio/jc-utils';
let arProxy1 = createArrayProxy([0,1,2,'3'])
console.log('arProxy1 =',arProxy1);
console.log('---------- 测试索引 ----------');
console.log('ar[1] =',arProxy1[1]);
console.log('ar[0] =',arProxy1[0]);
console.log('ar[0] =',arProxy1[-1]);
console.log('ar[-1] =',arProxy1[-1]);
console.log('ar[-2] =',arProxy1[-2]);
console.log('ar[-3] =',arProxy1[-3]);
console.log('---------- 测试length属性 ----------');
console.log('.length =',arProxy1.length);
console.log('---------- 测试 pop、push 等方法 ----------');
arProxy1.pop()
console.log('ar.pop() =',arProxy1);
arProxy1.push(3)
console.log('ar.push(3) =',arProxy1);
arProxy1.forEach((item)=>{
    console.log('item =',item);
})
console.log('---------- 测试 delete ----------');
console.log('delete arProxy1[1]:');
delete arProxy1[1]
console.log(arProxy1);
console.log('---------- 测试扩展的方法 ----------');
arProxy1.drop(2);
console.log('ar.drop(2) =',arProxy1);
console.log('print方法输出 arProxy2:');
const arProxy2 = createArrayProxy([
    0,
    [1,2,3,{}],
    {a:1,b:[1,2,3]},
    3
])
arProxy2.print()
console.log('---------- 测试 delete ----------');
console.log('delete arProxy2[1]:');
delete arProxy2[1]
console.log(arProxy2);

OutPuts:

arProxy1 = [ 0, 1, 2, '3' ]
---------- 测试索引 ----------
ar[1] = 1
ar[0] = 0
ar[0] = 3
ar[-1] = 3
ar[-2] = 2
ar[-3] = 1
---------- 测试length属性 ----------
.length = 4
---------- 测试 pop、push 等方法 ----------
ar.pop() = [ 0, 1, 2 ]
ar.push(3) = [ 0, 1, 2, 3 ]
---------- 测试迭代器 ----------
item = 0
item = 1
item = 2
item = 3
---------- 测试 delete ----------
delete arProxy1[1]:
[ 0, 2, 3 ]
---------- 测试扩展的方法 ----------
ar.drop(2) = [ 0, 2 ]
print方法输出 arProxy2:
[ 0, [ 1, 2, 3, {} ], { a: 1, b: [ 1, 2, 3 ] }, 3 ]
---------- 测试 delete ----------
delete arProxy2[1]:
[ 0, { a: 1, b: [ 1, 2, 3 ] }, 3 ]

2. Set 的代理创建器

当前未完成

3. Map 的代理创建器

当前未完成

18. TypeScript 类型工具

可以参考我的博客:《近五十个常用 TypeScript类型工具 的声明、描述、用法示例》https://blog.csdn.net/qq_28550263/article/details/130299891

注意

这组工具导出自 @jcstdio/jc-utils/ts

你可以这样导入(如):

import type { RequiredKeys } from '@jcstdio/jc-utils/ts'

包括以下类型工具:

类型工具 描述
RequiredKeys<T> 构造一个由所需的 T 的键组成的类型。
Mutable<T> 构造 T 的所有属性都设置为可变的类型。
RequiredExcept<T, K> 构造T的所有属性都设置为 required 的类型,k中指定的属性除外。
ReadonlyExcept<T, K> 构造 T 的所有属性都设置为 readonly 的类型,K中指定的属性除外。
MutableExcept<T, K> 构造一个类型,其中 T 的所有属性都设置为可变,K 中指定的属性除外。
PickByValue<T, V> 通过选取值为 V 类型的 T 的属性来构造类型。
OmitByValue<T, V> 通过省略 T 的值为 V 类型的属性来构造类型。
Intersection<T, U> 构造一个由 T 和 U 中的所有属性组成的类型。
Diff<T, U> 构造一个类型,它包含T中但U中没有的所有属性。
Subtract<T, U> 构造一个类型,由T中但不在 T 和 U 的交集中的所有属性组成。
Overwrite<T, U> 构造一个由 T 和 U 的所有属性组成的类型,其中 U 的属性覆盖 T 的同名属性。
UnionToIntersection<T> 构造一个类型,该类型由 T 中所有类型的交集中的所有属性组成。
FunctionKeys 造一个由T的键组成的类型,这些键是函数。
NonFunctionKeys<T> 构造一个由 T 的非函数键组成的类型。
PromiseType<T> 构造一个由 Promise-like 类型 T 的已解析类型组成的类型。
DeepReadonly<T> 构造一个 T 的所有属性都递归设置为只读的类型。
DeepPartial<T> 递归地构造 T 的所有属性都设置为可选的类型。
ValueOf<T> 构造由 T 中所有值的并集组成的类型。
OptionalKeys<T> 构造一个由 T 的可选键组成的类型。
ReadonlyKeys<T> 构造一个由 T 的只读键组成的类型。
WritableKeys 构造一个由 T 的可写键组成的类型。
DeepRequired<T> 递归地构造 T 的所有属性都设置为 required 的类型。
DeepWritable<T> 构造 T 的所有属性都设置为可递归写的类型。
DeepNonNullable 构造一个类型,其中 T 的所有属性都递归地设置为 non-nullable。
Immutable<T> 构造 T 的所有属性都设置为不可变的类型。
Unionize 构造 T 中所有可能的性质排列的联合类型。
Exclusive<T, U> 通过从 T 中挑选所有不属于 T 或 U 的属性来构造类型。
Intersect<T, U> 通过从 T 中选取所有也在 U 中的属性来构造类型。
Assign<T, U> 通过从 T 和 U 中选取所有属性并将它们合并在一起来构造一个类型。
DeepMutable<T> 构造一个 T 的所有属性都设置为可变递归的类型。
Promisable<T> 构造一个 T 的所有属性都设置为 Promisable 的类型。
Awaited<T> 构造一个类型,将 T 的所有属性设置为 awaited 类型。
Brand<T, U> 通过用附加的 U 类型包装 T 来构造一个标称类型。
NonUndefined<T> 通过从 T 中选取所有未定义的属性来构造类型。
Recordable<T = any> 构造一个类型,该类型是一个拥有字符串类型键和任意类型值的记录,类似于JavaScript中的对象。
ReadonlyRecordable<T = any> 构造一个类型,该类型是一个拥有字符串类型键和只读 T 类型值的记录,类似于JavaScript中的对象。
Indexable<T = any> 构造一个类型,该类型是一个拥有字符串类型键和任意类型值的记录,类似于JavaScript中的对象。
TimeoutHandle setTimeout 函数返回的计时器 ID 类型。
IntervalHandle setInterval 函数返回的计时器 ID 类型。
相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
1月前
|
JavaScript 前端开发 安全
TypeScript的优势与实践:提升JavaScript开发效率
【10月更文挑战第8天】TypeScript的优势与实践:提升JavaScript开发效率
|
1月前
|
JavaScript 前端开发 安全
深入理解TypeScript:增强JavaScript的类型安全性
【10月更文挑战第8天】深入理解TypeScript:增强JavaScript的类型安全性
45 0
|
1月前
|
JavaScript 前端开发 IDE
深入理解TypeScript:提升JavaScript开发的利器
【10月更文挑战第8天】 深入理解TypeScript:提升JavaScript开发的利器
30 0
|
14天前
|
JavaScript 前端开发 中间件
JS服务端技术—Node.js知识点
本文介绍了Node.js中的几个重要模块,包括NPM、Buffer、fs模块、path模块、express模块、http模块以及mysql模块。每部分不仅提供了基础概念,还推荐了相关博文供深入学习。特别强调了express模块的使用,包括响应相关函数、中间件、Router和请求体数据解析等内容。文章还讨论了静态资源无法访问的问题及其解决方案,并总结了一些通用设置。适合Node.js初学者参考学习。
30 1
|
19天前
|
开发框架 JavaScript 前端开发
Node.js日记:客户端和服务端介绍、Node.js介绍
Node.js日记:客户端和服务端介绍、Node.js介绍
|
19天前
|
Web App开发 JavaScript 前端开发
探索Deno:新一代JavaScript/TypeScript运行时环境
【10月更文挑战第25天】Deno 是一个新兴的 JavaScript/TypeScript 运行时环境,由 Node.js 创始人 Ryan Dahl 发起。本文介绍了 Deno 的核心特性,如安全性、现代化、性能和 TypeScript 支持,以及开发技巧和实用工具。Deno 通过解决 Node.js 的设计问题,提供了更好的开发体验,未来有望进一步集成 WebAssembly,拓展其生态系统。
|
23天前
|
JavaScript 前端开发 开发工具
Node.js——初识Node.js
Node.js——初识Node.js
20 4
|
23天前
|
JavaScript 前端开发 持续交付
构建现代Web应用:Vue.js与Node.js的完美结合
【10月更文挑战第22天】随着互联网技术的快速发展,Web应用已经成为了人们日常生活和工作的重要组成部分。前端技术和后端技术的不断创新,为Web应用的构建提供了更多可能。在本篇文章中,我们将探讨Vue.js和Node.js这两大热门技术如何完美结合,构建现代Web应用。
21 4
|
23天前
|
JavaScript 安全 前端开发
探索Deno 1.x:安全JavaScript/TypeScript运行时的新篇章
【10月更文挑战第21天】Deno 1.x 是由Node.js创始人Ryan Dahl发起的项目,旨在解决Node.js的安全和模块化问题。Deno 1.x 版本带来了统一的运行时、默认安全、ES模块支持和内置TypeScript支持等新特性。其安全模型基于最小权限原则、沙箱环境和严格的远程代码执行控制,适用于Web服务器、命令行工具、桌面和移动应用及微服务开发。本文探讨了Deno 1.x的核心特性、安全模型及其在现代Web开发中的应用。
|
29天前
|
JavaScript 安全 前端开发
掌握Deno:新一代安全的JavaScript和TypeScript运行时
【10月更文挑战第15天】Deno是由Node.js创始人Ryan Dahl发起的新一代JavaScript和TypeScript运行时,旨在解决Node.js的设计问题,提供更安全、现代的开发体验。本文介绍Deno的核心特性、优势及使用方法,包括安全性、统一的运行时、现代Web标准和内置工具等,帮助开发者快速上手Deno,适用于Web开发、工具开发和教育等领域。