Skip to content

JavaScript 常用工具函数

防抖 (Debounce)

触发事件后延迟 n 秒执行函数,若 n 秒内再次触发则重新计时(适用于输入框搜索、窗口 resize 等频繁触发的场景)。

javascript
/**
 * 防抖函数
 * @param {Function} func - 需要防抖的函数
 * @param {number} delay - 延迟时间(毫秒)
 * @param {boolean} immediate - 是否立即执行(首次触发时立即执行,之后防抖)
 * @returns {Function} 防抖处理后的函数
 */
function debounce(func, delay = 300, immediate = false) {
    let timer = null;
    return function (...args) {
        // 清除之前的定时器
        if (timer) clearTimeout(timer);

        // 立即执行模式:首次触发且无定时器时直接执行
        if (immediate && !timer) {
            func.apply(this, args);
        }

        // 重置定时器
        timer = setTimeout(() => {
            // 非立即执行模式:延迟后执行
            if (!immediate) {
                func.apply(this, args);
            }
            timer = null; // 执行后清空定时器
        }, delay);
    };
}

// 使用示例:输入框搜索防抖
const input = document.querySelector('input');
input.oninput = debounce((e) => {
    console.log('搜索:', e.target.value);
}, 500);
typescript
/**
 * 防抖函数
 * @param {Function} func - 需要防抖的函数
 * @param {number} delay - 延迟时间(毫秒)
 * @param {boolean} immediate - 是否立即执行(首次触发时立即执行,之后防抖)
 * @returns {Function} 防抖处理后的函数
 */
function debounce<T extends (...args: any[]) => any>(
    func: T,
    delay: number = 300,
    immediate: boolean = false
): (...args: Parameters<T>) => void {
    let timer: NodeJS.Timeout | null = null;

    return function (...args: Parameters<T>): void {
        if (timer) clearTimeout(timer);

        if (immediate && !timer) {
            func.apply(this, args);
        }

        timer = setTimeout(() => {
            if (!immediate) {
                func.apply(this, args);
            }
            timer = null;
        }, delay);
    };
}

// 使用示例
const input = document.querySelector('input');
if (input) {
    input.oninput = debounce((e: Event) => {
        console.log('搜索:', (e.target as HTMLInputElement).value);
    }, 500);
}

节流 (Throttle)

每隔 n 秒最多执行一次函数(适用于滚动加载、高频点击等需要限制执行频率的场景)。

javascript
/**
 * 节流函数(时间戳版)
 * @param {Function} func - 需要节流的函数
 * @param {number} interval - 间隔时间(毫秒)
 * @returns {Function} 节流处理后的函数
 */
function throttle(func, interval = 300) {
    let lastTime = 0; // 上次执行时间
    return function (...args) {
        const now = Date.now();
        // 若当前时间 - 上次执行时间 >= 间隔,则执行
        if (now - lastTime >= interval) {
            func.apply(this, args);
            lastTime = now; // 更新上次执行时间
        }
    };
}

// 使用示例:滚动监听节流
window.onscroll = throttle(() => {
    console.log('滚动位置:', window.scrollY);
}, 1000);
typescript
/**
 * 节流函数(时间戳版)
 * @param {Function} func - 需要节流的函数
 * @param {number} interval - 间隔时间(毫秒)
 * @returns {Function} 节流处理后的函数
 */
function throttle<T extends (...args: any[]) => any>(
    func: T,
    interval: number = 300
): (...args: Parameters<T>) => void {
    let lastTime = 0;

    return function (...args: Parameters<T>): void {
        const now = Date.now();
        if (now - lastTime >= interval) {
            func.apply(this, args);
            lastTime = now;
        }
    };
}

// 使用示例
window.onscroll = throttle(() => {
    console.log('滚动位置:', window.scrollY);
}, 1000);

深克隆 (Deep Clone)

完全复制一个对象(包括嵌套对象、数组等),避免原对象被修改时影响克隆对象(解决浅拷贝的引用问题)。

javascript
/**
 * 深克隆函数(支持对象、数组、日期、正则等)
 * @param {*} target - 需要克隆的值
 * @returns {*} 克隆后的新值
 */
function deepClone(target) {
    // 基本类型直接返回(null 特殊处理)
    if (target === null || typeof target !== 'object') {
        return target;
    }

    let cloneResult;

    // 处理日期
    if (target instanceof Date) {
        cloneResult = new Date();
        cloneResult.setTime(target.getTime());
        return cloneResult;
    }

    // 处理正则
    if (target instanceof RegExp) {
        cloneResult = new RegExp(target.source, target.flags);
        return cloneResult;
    }

    // 处理数组或对象(根据原类型创建新容器)
    cloneResult = Array.isArray(target) ? [] : {};

    // 递归克隆属性
    for (const key in target) {
        if (target.hasOwnProperty(key)) { // 只克隆自身属性(排除原型链)
            cloneResult[key] = deepClone(target[key]);
        }
    }

    return cloneResult;
}

// 使用示例
const obj = {a: 1, b: {c: 2}, d: [3, 4]};
const cloneObj = deepClone(obj);
cloneObj.b.c = 100;
console.log(obj.b.c); // 2(原对象不受影响)
typescript
/**
 * 深克隆函数(支持对象、数组、日期、正则等)
 * @param {T} target - 需要克隆的值
 * @returns {T} 克隆后的新值
 */
function deepClone<T>(target: T): T {
    // 基本类型直接返回
    if (target === null || typeof target !== 'object') {
        return target;
    }

    let cloneResult: any;

    // 处理日期
    if (target instanceof Date) {
        cloneResult = new Date();
        cloneResult.setTime(target.getTime());
        return cloneResult as T;
    }

    // 处理正则
    if (target instanceof RegExp) {
        cloneResult = new RegExp(target.source, target.flags);
        return cloneResult as T;
    }

    // 处理数组或对象
    if (Array.isArray(target)) {
        cloneResult = [] as T extends Array<any> ? T : never;
    } else {
        cloneResult = {} as T;
    }

    // 递归克隆属性
    for (const key in target) {
        if (Object.prototype.hasOwnProperty.call(target, key)) {
            cloneResult[key] = deepClone(target[key]);
        }
    }

    return cloneResult;
}

// 使用示例
const obj = {a: 1, b: {c: 2}, d: [3, 4]};
const cloneObj = deepClone(obj);
cloneObj.b.c = 100;
console.log(obj.b.c); // 2(原对象不受影响)

检测数据类型 (Check Type)

精确判断变量的类型(比 typeof 更准确,如区分数组、对象、null 等)。

javascript
/**
 * 检测数据类型
 * @param {*} value - 需要检测的值
 * @returns {string} 类型字符串(如 'array'、'object'、'null' 等)
 */
function checkType(value) {
    // 利用 Object.prototype.toString 精确获取类型
    const typeStr = Object.prototype.toString.call(value);
    // 提取类型(如 "[object Array]" → "array")
    return typeStr.slice(8, -1).toLowerCase();
}

// 使用示例
console.log(checkType([])); // 'array'
console.log(checkType({})); // 'object'
console.log(checkType(null)); // 'null'
console.log(checkType(123)); // 'number'
console.log(checkType(async () => {
})); // 'asyncfunction'
typescript
/**
 * 检测数据类型
 * @param {unknown} value - 需要检测的值
 * @returns {string} 类型字符串(如 'array'、'object'、'null' 等)
 */
function checkType(value: unknown): string {
    const typeStr = Object.prototype.toString.call(value);
    return typeStr.slice(8, -1).toLowerCase();
}

// 使用示例
console.log(checkType([])); // 'array'
console.log(checkType({})); // 'object'
console.log(checkType(null)); // 'null'
console.log(checkType(123)); // 'number'
console.log(checkType(async () => {
})); // 'asyncfunction'

格式化日期 (Format Date)

将时间戳或 Date 对象格式化为指定字符串(如 YYYY-MM-DD HH:MM:SS)。

javascript
/**
 * 格式化日期
 * @param {Date|number} date - 日期(Date对象或时间戳)
 * @param {string} format - 格式字符串(YYYY-MM-DD HH:MM:SS)
 * @returns {string} 格式化后的日期字符串
 */
function formatDate(date, format = 'YYYY-MM-DD HH:MM:SS') {
    if (typeof date === 'number') {
        date = new Date(date); // 时间戳转 Date 对象
    }
    if (!(date instanceof Date) || isNaN(date.getTime())) {
        throw new Error('无效的日期');
    }

    const o = {
        'Y+': date.getFullYear(), // 年
        'M+': date.getMonth() + 1, // 月(0-11,需+1)
        'D+': date.getDate(), // 日
        'H+': date.getHours(), // 时
        'm+': date.getMinutes(), // 分
        's+': date.getSeconds() // 秒
    };

    // 替换格式中的年、月、日等(补零处理)
    for (const k in o) {
        const reg = new RegExp(`(${k})`);
        if (reg.test(format)) {
            const value = o[k].toString();
            // 若格式为 YY,则取年份后两位;否则补零至对应长度(如 M 转 09)
            format = format.replace(reg,
                RegExp.$1.length === 1 ? value : value.padStart(RegExp.$1.length, '0')
            );
        }
    }

    return format;
}

// 使用示例
console.log(formatDate(new Date())); // 2025-10-17 15:30:45
console.log(formatDate(1753000000000, 'MM/DD/YYYY')); // 06/18/2025
typescript
type DateFormat = 'YYYY-MM-DD' | 'YYYY-MM-DD HH:MM:SS' | 'MM/DD/YYYY' | string;

/**
 * 格式化日期
 * @param {Date|number} date - 日期(Date对象或时间戳)
 * @param {DateFormat} format - 格式字符串(YYYY-MM-DD HH:MM:SS)
 * @returns {string} 格式化后的日期字符串
 */
function formatDate(
    date: Date | number,
    format: DateFormat = 'YYYY-MM-DD HH:MM:SS'
): string {
    if (typeof date === 'number') {
        date = new Date(date);
    }

    if (!(date instanceof Date) || isNaN(date.getTime())) {
        throw new Error('无效的日期');
    }

    const o: Record<string, number> = {
        'Y+': date.getFullYear(),
        'M+': date.getMonth() + 1,
        'D+': date.getDate(),
        'H+': date.getHours(),
        'm+': date.getMinutes(),
        's+': date.getSeconds()
    };

    for (const k in o) {
        const reg = new RegExp(`(${k})`);
        if (reg.test(format)) {
            const value = o[k].toString();
            format = format.replace(
                reg,
                RegExp.$1.length === 1 ? value : value.padStart(RegExp.$1.length, '0')
            );
        }
    }

    return format;
}

// 使用示例
console.log(formatDate(new Date())); // 2025-10-17 15:30:45
console.log(formatDate(1753000000000, 'MM/DD/YYYY')); // 06/18/2025

尘埃虽微,积之成集;问题虽小,记之为鉴。 雾中低语,心之所向;思绪飘渺,皆可成章。