utils工具函数

116 阅读2分钟
(function () {
    let class2type = {},
        toString = class2type.toString,
        hasOwn = class2type.hasOwnProperty,
        fnToString = hasOwn.toString,
        ObjectFunctionString = fnToString.call(Object),
        getProto = Object.getPrototypeOf;

    // 检测数据类型的通用方法
    const toType = function toType(obj) {
        if (obj == null) return obj + "";
        return typeof obj === "object" || typeof obj === "function" ?
            /^\[object (\w+)\]$/g.exec(toString.call(obj))[1].toLowerCase() :
            typeof obj;
    };

    // 检测是否为函数
    const isFunction = function isFunction(obj) {
        // 排除部分浏览器中
        //   + typeof document.createElement("object")==="function"
        //   + typeof document.getElementsByTagName("div")==="function"
        return typeof obj === "function" && typeof obj.nodeType !== "number" &&
            typeof obj.item !== "function";
    };

    // 检测是否为window对象
    const isWindow = function isWindow(obj) {
        return obj != null && obj === obj.window;
    };

    // 检测是否为数组或者类数组
    const isArrayLike = function isArrayLike(obj) {
        let length = !!obj && "length" in obj && obj.length,
            type = toType(obj);
        if (isFunction(obj) || isWindow(obj)) return false;
        return type === "array" || length === 0 ||
            typeof length === "number" && length > 0 && (length - 1) in obj;
    };

    // 检测是否为纯粹的对象「标准普通对象 或者 直属类是Object」
    const isPlainObject = function isPlainObject(obj) {
        let proto, Ctor;
        if (!obj || toString.call(obj) !== "[object Object]") return false;
        proto = getProto(obj);
        if (!proto) return true; //Object.create(null)
        Ctor = hasOwn.call(proto, "constructor") && proto.constructor;
        return typeof Ctor === "function" && fnToString.call(Ctor) === ObjectFunctionString;
    };

    // 检测是否为空对象
    const isEmptyObject = function isEmptyObject(obj) {
        if (obj == null) return false;
        if (typeof obj !== "object") return false;
        let keys = Object.keys(obj);
        keys = keys.concat(Object.getOwnPropertySymbols(obj));
        return keys.length === 0;
    };

    // 检测是不是有效数字
    const isNumeric = function isNumeric(obj) {
        let type = toType(obj);
        return (type === "number" || type === "string") && !isNaN(obj);
    };

    // 函数防抖
    const clearTimer = function clearTimer(timer) {
        if (timer) clearTimeout(timer);
        return null;
    };
    const debounce = function debounce(func, wait, immediate) {
        if (typeof func !== 'function') throw new TypeError('func is not a function~');
        if (typeof wait === 'boolean') immediate = wait;
        if (typeof wait !== 'number') wait = 300;
        if (typeof immediate !== "boolean") immediate = false;
        let timer = null;
        return function operate(...params) {
            let now = !timer && immediate,
                result;
            timer = clearTimer(timer);
            timer = setTimeout(() => {
                if (!immediate) func.call(this, ...params);
                timer = clearTimer(timer);
            }, wait);
            if (now) result = func.call(this, ...params);
            return result;
        };
    };

    // 函数节流
    const throttle = function throttle(func, wait) {
        if (typeof func !== 'function') throw new TypeError('func is not a function~');
        if (typeof wait !== 'number') wait = 300;
        let timer = null,
            previous = 0;
        return function operate(...params) {
            let now = +new Date(),
                remaining = wait - (now - previous),
                result;
            if (remaining <= 0) {
                result = func.call(this, ...params);
                previous = +new Date();
                timer = clearTimer(timer);
            } else if (!timer) {
                timer = setTimeout(() => {
                    func.call(this, ...params);
                    previous = +new Date();
                    timer = clearTimer(timer);
                }, remaining);
            }
            return result;
        };
    };

    // 迭代数组/类数组/对象
    const each = function each(obj, callback) {
        let length,
            i = 0,
            keys;
        if (isArrayLike(obj)) {
            length = obj.length;
            for (; i < length; i++) {
                if (callback.call(obj[i], obj[i], i) === false) break;
            }
        } else {
            keys = Object.keys(obj);
            if (typeof Symbol !== "undefined") keys.concat(Object.getOwnPropertySymbols(obj));
            for (i = 0; i < keys.length; i++) {
                let key = keys[i],
                    item = obj[key];
                if (callback.call(item, item, key) === false) break;
            }
        }
        return obj;
    };

    // 实现数组/对象的深浅拷贝
    const clone = function clone() {
        // 处理传递的实参  target:克隆的目标  deep:控制深浅克隆
        let target = arguments[0],
            len = arguments.length,
            deep = false,
            treated = arguments[len - 1];
        if (typeof target === "boolean" && len >= 2) {
            deep = target;
            target = arguments[1];
        }

        // 防止套娃操作
        if (!Array.isArray(treated) || !treated.treated) {
            treated = [];
            treated.treated = true;
        }
        if (treated.includes(target)) return target;
        treated.push(target);

        // 检测类型
        let type = toType(target),
            isArray = Array.isArray(target),
            isObject = isPlainObject(target);

        // 特殊值的克隆「含:原始值、特殊对象...」
        if (target == null) return target;
        let ctor = target.constructor;
        if (/^(regexp|date)$/i.test(type)) return new ctor(target);
        if (/^(error)$/i.test(type)) return new ctor(target.message);
        if (isFunction(target)) return function proxy(...params) {
            return target.call(this, ...params);
        };
        if (!isObject && !isArray) return target;

        // 数组和对象的克隆
        let result = new ctor();
        each(target, (copy, key) => {
            // copy:要拷贝的属性值  key:属性名
            if (deep) {
                // 深拷贝
                result[key] = clone(deep, copy, treated);
                return;
            }
            // 浅拷贝
            result[key] = copy;
        });
        return result;
    };

    // 数组和对象的深浅合并
    const merge = function merge() {
        let options,
            target = arguments[0] || {},
            i = 1,
            length = arguments.length,
            deep = false,
            treated = arguments[length - 1];
        // 第一次执行merge,最后一项是用来替换TARGET的,不是用来记录谁处理过、谁没处理过的,所以我们要为其赋值一个新数组;等到后期每次递归,我们都会在最末尾把存放哪些处理过的数组传递过来,此时最后一项这个数组才不是用来替换TARGET的!! => treated.treated有这个属性的数组是专门存放哪些处理过的
        Array.isArray(treated) && treated.treated ? length-- : (treated = [], treated.treated = true);
        // 如果第一个值是布尔,要把这个值给DEEP,让TARGET存储的第二个传递的参数(也就是第一个对象),也就是要被替换的对象
        if (typeof target === "boolean") {
            deep = target;
            target = arguments[i] || {};
            i++;
        }
        // 确保TARGET是个对象
        if (typeof target !== "object" && !isFunction(target)) target = {};
        // 循环除第一个传递的对象外,剩下的每个传递的对象
        for (; i < length; i++) {
            options = arguments[i];
            if (options == null) continue;
            // 之前已经处理过这个对象的,就没必要在处理了;没处理过的,加入到treated处理列表中!
            if (treated.includes(options)) return options;
            treated.push(options);
            // 循环这个对象中的每一项,用每一项的值替换TARGET中对应项的值
            each(options, function (copy, name) {
                let copyIsArray = Array.isArray(copy),
                    copyIsObject = isPlainObject(copy),
                    src = target[name],
                    clone = src;
                // 如果某一项的值是纯粹对象或者数组,并且DEEP是TURE,我们开启深度合并
                if (deep && copy && (copyIsArray || copyIsObject)) {
                    if (copyIsArray && !Array.isArray(clone)) clone = [];
                    if (copyIsObject && !isPlainObject(clone)) clone = {};
                    target[name] = merge(deep, clone, copy, treated);
                } else if (copy !== undefined) {
                    target[name] = copy;
                }
            });
        }
        return target;
    };

    let utils = {
        debounce,
        throttle,
        toType,
        isFunction,
        isWindow,
        isArrayLike,
        isPlainObject,
        isEmptyObject,
        isNumeric,
        clone,
        merge,
        each
    };

    /* 导出模块 */
    if (typeof module === "object" && typeof module.exports === "object") module.exports = utils;
    if (typeof window !== 'undefined') window.utils = utils;
})();```