常用工具函数(JavaScript)

4 阅读2分钟

1. 选择器

function selector() {
    var ls = document.querySelectorAll(arguments[0]);
    if (!ls) return null;
    if (ls.length === 1) return ls[0];
    return ls;
}

2. 动态计算 rem(适配移动端)

(function () {
    var root = document.documentElement;
    function init() {
        var width = root.clientWidth || window.innerWidth;
        width = width < 320 ? 320 : width; // 最小屏幕设定为320px
        root.style.fontSize = width >= 640 ? '100px' : (width / 3.75 + 'px'); // 最大屏幕640px;标准375px
        document.body.style.fontSize = '16px';
    }
    window.addEventListener('orientationchange' in window ? 'orientationchange' : 'resize', init, false);
    document.addEventListener('DOMContentLoaded', init, false);
})();

3. ES5 模块导出兼容写法

(function (global, factory) {
    "use strict"
    if (typeof module === "object" && typeof module.exports === "object") {
        module.exports = factory(global, true)
    } else if (typeof define === 'function' && define.amd) {
        define(function () { return factory(global) })
    } else {
        factory(global)
    }
})(typeof window !== "undefined" ? window : this, function (context) {
    // 模块内容
});

4. Ajax 封装

GET 请求

/**
 * @param {string} url 请求地址
 * @param {string} params key=value&key=value 参数字符串
 * @param {Function} fn 回调函数
 */
function get(url, params, fn) {
    var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
    if (!xhr) return;
    xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
            fn(xhr.status === 200 ? null : xhr.status, xhr.responseText);
        }
    };
    xhr.open('GET', url + '?' + params, true);
    xhr.send(null);
}

POST 请求

/**
 * @param {string} url 请求地址
 * @param {string|FormData} params 参数
 * @param {Function} fn 回调函数
 */
function post(url, params, fn) {
    var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
    if (!xhr) return;
    xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
            fn(xhr.status === 200 ? null : xhr.status, xhr.responseText);
        }
    };
    if (!(params instanceof FormData)) {
        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    }
    xhr.open('POST', url, true);
    xhr.send(params);
}

5. 防抖(Debounce)

/**
 * @param {Function} callback
 * @param {Number} delay 延时(毫秒)
 * @param {Boolean} immediate 立即执行
 */
function Debounce(callback, delay = 1000, immediate = false) {
    if (!callback) return;
    var timeout, context;
    return function () {
        context = this;
        if (timeout) clearTimeout(timeout);
        if (immediate) {
            return callback.apply(context, arguments);
        } else {
            timeout = setTimeout(() => callback.apply(context, arguments), delay);
        }
    };
}

6. 节流(Throttle)

/**
 * @param {Function} callback
 * @param {Number} delay 延时(毫秒)
 * @param {Boolean} immediate 立即执行
 */
function Throttle(callback, delay = 500, immediate = false) {
    if (!callback) return;
    let lastTime, timer, context, now;
    return function () {
        context = this;
        now = +new Date();
        if (immediate) return callback.apply(context, arguments);
        if (lastTime && now < lastTime + delay) {
            clearTimeout(timer);
            timer = setTimeout(() => {
                lastTime = now;
                callback.apply(context, arguments);
            }, delay);
        } else {
            lastTime = now;
            callback.apply(context, arguments);
        }
    };
}

7. 圣杯继承模式

const inherit = (Target, Origin) => {
    Target.prototype = new Origin();
    Target.prototype.constructor = Target;
    Target.prototype.$prototype = Origin.prototype;
};

// 示例略

8. 获取滚动条距离

function getScrollOffset() {
    if (window.pageXOffset) {
        return { x: window.pageXOffset, y: window.pageYOffset };
    } else {
        return {
            x: document.body.scrollLeft + document.documentElement.scrollLeft,
            y: document.body.scrollTop + document.documentElement.scrollTop
        };
    }
}

9. 获取视口尺寸

function getViewportOffset() {
    if (window.innerWidth) {
        return { w: window.innerWidth, h: window.innerHeight };
    } else {
        if (document.compatMode === "BackCompat") {
            return { w: document.body.clientWidth, h: document.body.clientHeight };
        } else {
            return { w: document.documentElement.clientWidth, h: document.documentElement.clientHeight };
        }
    }
}

10. 获取元素任意 CSS 属性

function getStyle(elem, prop) {
    return window.getComputedStyle ? window.getComputedStyle(elem, null)[prop] : elem.currentStyle[prop];
}

11. 事件绑定/解绑(兼容 IE)

function addEvent(elem, type, handle) {
    if (elem.addEventListener) {
        elem.addEventListener(type, handle, false);
    } else if (elem.attachEvent) {
        elem.attachEvent('on' + type, function () { handle.call(elem); });
    } else {
        elem['on' + type] = handle;
    }
}

function removeEvent(elem, type, handle) {
    if (elem.removeEventListener) {
        elem.removeEventListener(type, handle, false);
    } else if (elem.detachEvent) {
        elem.detachEvent('on' + type, handle);
    } else {
        elem['on' + type] = null;
    }
}

12. 阻止事件冒泡

function stopBubble(e) {
    if (e && e.stopPropagation) {
        e.stopPropagation();
    } else {
        window.event.cancelBubble = true;
    }
}

13. 判断回文字符串

function isPalindrome(str) {
    str = str.replace(/\W/g, '').toLowerCase();
    return str === str.split('').reverse().join('');
}

14. 异步加载脚本

function loadScript(url, callback) {
    var el = document.createElement('script');
    if (el.readyState) {
        el.onreadystatechange = function () {
            if (el.readyState === 'complete' || 'loaded') callback();
        };
    } else {
        el.onload = callback;
    }
    el.src = url;
    document.body.appendChild(el);
}

15. Cookie 管理

const docCookies = {
    get: function (key) {
        return decodeURIComponent(
            document.cookie.replace(
                new RegExp('(?:(?:^|.*;)\\s*' + encodeURIComponent(key).replace(/[-.+*]/g, '\\$&') + '\\s*\\=\\s*([^;]*).*$)|^.*$'), '$1')
        ) || ''
    },
    set: function (key, sValue, vEnd, sPath, sDomain, bSecure) {
        if (!key || /^(?:expires|max-age|path|domain|secure)$/i.test(key)) { return false }
        let sExpires = ''
        if (vEnd) {
            switch (vEnd.constructor) {
                case Number:
                    sExpires = vEnd === Infinity ? '; expires=Fri, 31 Dec 9999 23:59:59 GMT' : '; max-age=' + vEnd
                    break
                case String:
                    sExpires = '; expires=' + vEnd
                    break
                case Date:
                    sExpires = '; expires=' + vEnd.toUTCString()
                    break
            }
        }
        const cookieParts = [
            encodeURIComponent(key) + '=' + encodeURIComponent(sValue),
            sExpires,
            sDomain ? '; domain=' + sDomain : '',
            sPath ? '; path=' + sPath : '',
            bSecure ? '; secure' : ''
        ].filter(Boolean)
        document.cookie = cookieParts.join('')
        return true
    },
    remove: function (key, sPath = '/', sDomain = '.videoseek.ai') {
        if (!key || !this.has(key)) { return false }
        const cookieParts = [
            encodeURIComponent(key) + '=; expires=' + new Date('1970-01-01').toString(),
            sDomain ? '; domain=' + sDomain : '',
            sPath ? '; path=' + sPath : ''
        ].filter(Boolean)
        document.cookie = cookieParts.join('')
        return true
    },
    has: function (key) {
        return (new RegExp('(?:^|;\\s*)' + encodeURIComponent(key).replace(/[-.+*]/g, '\\$&') + '\\s*\\=')).test(document.cookie)
    },
    keys: function () {
        const aKeys = document.cookie.replace(/((?:^|\s*;)[^=]+)(?=;|$)|^\s*|\s*(?:=[^;]*)?(?:1|$)/g, '').split(/\s*(?:=[^;]*)?;\s*/)
        for (let nIdx = 0; nIdx < aKeys.length; nIdx++) { aKeys[nIdx] = decodeURIComponent(aKeys[nIdx]) }
        return aKeys
    }
}

完整实现见原文。


16. 手写 call / apply / bind

Function.prototype._call = function () {
    var args = [].slice.call(arguments)
    var ctx = args.shift() || window
    ctx.fn = this
    var result = ctx.fn(...args)
    delete ctx.fn
    return result
}
Function.prototype._apply = function () {
    var ctx = arguments[0] || window
    var args = arguments[1]
    ctx.fn = this
    args = Array.isArray(args) ? args : []
    var result = ctx.fn(...args)
    delete ctx.fn
    return result
}
Function.prototype._bind = function (otherThis) {
    if (typeof this !== 'function') return
    var baseArgs = [].slice.call(arguments, 1),
        ctx = this,
        Temp = function () { },
        F = function () {
            baseArgs = baseArgs.concat(arguments)
            return ctx.apply(
                Temp.prototype.isPrototypeOf(this) ? this : otherThis, baseArgs
            )
        }
    if (this.prototype) Temp.prototype = this.prototype
    F.prototype = new Temp()
    return F
}

17. requestAnimationFrame 兼容

window.requestAnimFrame = (function () {
    return window.requestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        function (callback) { setTimeout(callback, 1000 / 60); };
})();

window.cancelAnimFrame = (function () {
    return window.cancelAnimationFrame ||
        window.webkitCancelAnimationFrame ||
        window.mozCancelAnimationFrame ||
        function (id) { clearTimeout(id); };
})();

18. JSONP 请求

function jsonp(url) {
    var el = document.createElement('script');
    el.src = url;
    document.body.appendChild(el);
}

19. 工具判断函数

判断有值

function isValue(v) {
    return !(v === undefined || v === 'undefined' || v === null || v === 'null');
}

获取精确类型

function toType(v) {
    return Object.prototype.toString.call(v).replace(/$$|$$/g, '').split(/\s/)[1].toLowerCase();
}

判断数字

function isNumber(v) {
    return !isNaN(v);
}

判断 Promise

function isPromise(v) {
    return v && typeof v === 'object' && typeof v.then === 'function';
}

判断 DOM 节点

function isDom(v) {
    return typeof HTMLElement === 'undefined'
        ? v && typeof v === 'object' && v.nodeType !== undefined
        : v instanceof HTMLElement;
}

判断空值(数组/对象/字符串等)

function isEmpty(v) {
    if (Array.isArray(v) || typeof v === 'string') return v.length === 0;
    if (v instanceof Map || v instanceof Set) return v.size === 0;
    if (v && typeof v === 'object') return Object.keys(v).length === 0;
    return false;
}

20. 随机字符串生成

const CHARACTER = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
function anystr(range = 32) {
    const r = [];
    for (let i = 0; i < range; i++) {
        r.push(CHARACTER.charAt(Math.floor(Math.random() * CHARACTER.length)));
    }
    return r.join('');
}
// 可使用 window.crypto.randomUUID 替代

21. 模板字符串替换

function format(s, args) {
    if (!s || !args) return s;
    if (Array.isArray(args)) {
        args.forEach((v, i) => s = s.replace(new RegExp("({[" + i + "]})", "g"), v));
    } else if (typeof args === 'object') {
        Object.keys(args).forEach(k => s = s.replace(new RegExp("({" + k + "})", "g"), args[k]));
    }
    return s;
}

22. 单词首字母大写

function capitalize(v) {
    return String(v).toLowerCase().replace(/( |^)[a-z]/g, s => s.toUpperCase());
}

23. 中文 Unicode 编码/解码

const unicode = {
    encode(v) {
        if (!v) return null;
        v = String(v);
        const r = [];
        for (let i = 0; i < v.length; i++) {
            r.push(('00' + v.charCodeAt(i).toString(16)).slice(-4));
        }
        return '\\u' + r.join('\\u');
    },
    decode(v) {
        return v ? unescape(v.replace(/\\/g, '%')) : null;
    }
};

24. URL 参数处理

const urlParams = {
    queryString(o) {
        if (!o || typeof o !== 'object') return '';
        return Object.keys(o).map(k => {
            let v = o[k];
            if (v === undefined) v = null;
            if (v && typeof v === 'object') v = JSON.stringify(v);
            return `${k}=${v}`;
        }).join('&');
    },
    get(url, name) {
        url = decodeURIComponent(url || location.search).replace(/^\?/, '');
        if (!url) return name ? '' : {};
        const o = JSON.parse('{"' + url.replace(/"/g, '\\"').replace(/&/g, '","').replace(/=/g, '":"') + '"}');
        return name ? (o[name] === undefined ? '' : o[name]) : o;
    }
};
// 可使用 new URLSearchParams 替代

25. 对象操作

浅拷贝合并

function objectMerge() {
    const args = [].slice.call(arguments);
    const target = Object(args.shift());
    args.forEach(o => {
        if (!o || typeof o !== 'object') return;
        Object.keys(o).forEach(k => {
            if (typeof o[k] !== 'undefined') target[k] = o[k];
        });
    });
    return target;
}

粗暴深克隆(不支持函数/循环引用)

该方法有缺陷,“循环引用”(会报错)和函数无法克隆

function objectFormat(o) {
    return JSON.parse(JSON.stringify(o || {}));
}
// 可使用 window.structuredClone 或者 lodash.merge 替代

深度克隆(支持嵌套对象/数组)

function deepClone() {
    const args = [].slice.call(arguments);
    const target = Object(args.shift());
    args.forEach(items => {
        if (!items || typeof items !== 'object') return;
        Object.keys(items).forEach(key => {
            if (key === '__proto__') return;
            const row = items[key];
            if (row === undefined) return;
            if (Array.isArray(row)) {
                if (!Array.isArray(target[key])) {
                    target[key] = [...row];
                    return;
                }
                row.forEach((col, index) => {
                    target[key][index] = typeof col === 'object' ? deepClone(target[key][index], col) : col;
                });
            } else if (typeof row === 'object') {
                target[key] = deepClone(target[key] || {}, row);
            } else {
                target[key] = row;
            }
        });
    });
    return target;
}

26. 本地存储封装

const storage = {
    session(name, v) {
        if (v == null) return sessionStorage.getItem(name);
        sessionStorage.setItem(name, typeof v === 'object' ? JSON.stringify(v) : v);
        return true;
    },
    local(name, v) {
        if (v == null) return localStorage.getItem(name);
        localStorage.setItem(name, typeof v === 'object' ? JSON.stringify(v) : v);
        return true;
    }
};

27. 倒计时类

class Countdown {
    constructor(longtime = 120, delay = 1000, callback = () => {}) {
        this.longtime = longtime;
        this.delay = delay;
        this.fn = callback;
        this.stepper = 0;
    }
    countdown() {
        this.fn(this.stepper);
        this.timer = setInterval(() => {
            if (this.longtime <= this.stepper) return clearInterval(this.timer);
            this.stepper++;
            this.fn(this.stepper);
        }, this.delay);
    }
    cancel() {
        clearInterval(this.timer);
    }
}

28. 驼峰与下划线互转

// 下划线 → 驼峰
function toHump(name) {
    return name.replace(/\_(\w)/g, (_, letter) => letter.toUpperCase());
}

// 驼峰 → 下划线
function toLine(name) {
    return name.replace(/([A-Z])/g, "_$1").toLowerCase();
}

✅ 以上为常用 JavaScript 工具函数整理,适用于兼容性要求高或无构建工具的项目。建议在现代项目中优先使用 ES6+ 语法及模块化方案。