(function () {
    "use strict";
    const class2type = {};
    const getProto = Object.getPrototypeOf;
    const toString = class2type.toString; //->Object.prototype.toString
    const hasOwn = class2type.hasOwnProperty; //->Object.prototype.hasOwnProperty
    const fnToString = hasOwn.toString; //->Function.prototype.toString
    const ObjectFunctionString = fnToString.call(Object);
    //->Object.toString() //->"function Object() { [native code] }"

    Const arr_type = ["Boolean", "Number", "String", "Function", "Array", "Date", "RegExp", "Object", "Error", "Symbol", "BigInt"]; arr_type.forEach(function (name) { class2type["[object " + name + "]"] = name.toLowerCase(); }); Const toType = function toType(obj) {if (obj == null) return obj + ""; return typeof obj === "object" || typeof obj === "function" ? class2type[toString.call(obj)] || "object" : typeof obj; }; * /
    
    // Check the data type
    const toType = function toType(obj) {
        if (obj == null) return obj + "";
        const reg = /^\[object ([a-zA-Z0-9]+)\]$/i;
        return typeof obj === "object" || typeof obj === "function" ?
            reg.exec(toString.call(obj))[1].toLowerCase() :
            typeof obj;
    };


    // Check if it is a function
    const isFunction = function isFunction(obj) {
        // In some browsers, typeof returns "function" for HTML <object> elements
        // (i.e., `typeof document.createElement( "object" ) === "function"`).
        return typeof obj === "function" && typeofobj.nodeType ! = ="number";
    };

    // Check whether it is Window
    const isWindow = function isWindow(obj) {
        returnobj ! =null && obj === obj.window;
    };

    // Check whether it is an array or a class array
    const isArrayLike = function isArrayLike(obj) {
        constlength = !! 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;
    };

    / / check as pure Object (obj. __proto__ = = = Object. The prototype | | Object. The create (null))
    const isPlainObject = function isPlainObject(obj) {
        const 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;
    };

    // Check whether the current object is empty
    const isEmptyObject = function isEmptyObject(obj) {
        if (obj == null) return false;
        const keys = Object.keys(obj);
        if (typeof Symbol! = ="undefined") {
            keys = keys.concat(Object.getOwnPropertySymbols(obj));
        }
        return keys.length === 0;
    };

    // Check whether it is a valid digit, consider: 10 and "10" are valid digits, but true/null are not
    const isNumeric = function isNumeric(obj) {
        const type = toType(obj);
        return (type === "number" || type === "string") &&!isNaN(obj);
    };

    // Iterate over each item in the array/class array/object
    const each = function each(obj, callback) {
        if (typeofcallback ! = ="function") callback = Function.prototype;
        let i = 0,
            len,
            item,
            keys,
            key;
        if (isArrayLike(obj)) {
            len = obj.length;
            for (; i < len; i++) {
                item = obj[i];
                // We dealt with "end-of-loop control" not supported by for-each: the callback returns false
                if (callback.call(item, item, i) === false) break; }}else {
            keys = Object.keys(obj);
            // We consider the FOR IN BUG
            if (typeof Symbol! = ="undefined") {
                keys = keys.concat(Object.getOwnPropertySymbols(obj))
            };
            for (; i < keys.length; i++) {
                key = keys[i];
                item = obj[key];
                if (callback.call(item, item, key) === false) break; }}return obj;
    };

    // Implement "array/pure object" depth mergeTrain of thought1:const merge = function merge() {
        const options, src, copyIsArray,
            target = arguments[0] || {},
            i = 1,
            length = arguments.length,
            deep = false;
        if (typeof target === "boolean") {
            deep = target;
            target = arguments[i] || {};
            i++;
        }
        if (typeoftarget ! = ="object" && !isFunction(target)) target = {};
        for (; i < length; i++) {
            if ((options = arguments[i]) ! =null) {
                each(options, function (copy, name) {
                    // Prevent dead recursion
                    if (target === copy) return;
                    copyIsArray = Array.isArray(copy);
                    if (deep && copy && (isPlainObject(copy) || copyIsArray)) {
                        src = target[name];
                        if (copyIsArray && !Array.isArray(src)) {
                            src = [];
                        } else if(! copyIsArray && ! isPlainObject(src)) { src = {}; } target[name] = merge(deep, src, copy);return; } target[name] = copy; }); }}returntarget; }; Train of thought2:const merge = function merge() {
        let options,
            target = arguments[0] || {},
            i = 1,
            length = arguments.length,
            deep = false,
            treated = arguments[length - 1];
        // Merge the first time, the last item is used to replace the TARGET, is not used to record who is processed and who is not processed,
        // So we assign a new array to it;
        // At the end of each recursion, we pass in the processed array,
        // This array is not used to replace TARGET!!
        //treated. Treated has this property in an array that stores which treatments are used
        if (Array.isArray(treated) && treated.treated) {
            length--;
        } else {
            treated = [];
            treated.treated = true;
        }
        // If the first value is a Boolean, give it to DEEP and let TARGET store the second passed argument
        // (the first object), the object to be replaced
        if (typeof target === "boolean") {
            deep = target;
            target = arguments[i] || {};
            i++;
        }
        // Make sure TARGET is an object
        if (typeoftarget ! = ="object" && !isFunction(target)) target = {};
        // Loop through each passed object except the first
        for (; i < length; i++) {
            options = arguments[i];
            if (options == null) continue;
            // If you have already handled this object, there is no need to handle it again; Untreated, added to treated list!
            if (treated.includes(options)) return options;
            treated.push(options);
            // Loop over each item in the object, replacing the corresponding item in TARGET with the value of each item
            each(options, function (copy, name) {
                let copyIsArray = Array.isArray(copy),
                    copyIsObject = isPlainObject(copy),
                    src = target[name],
                    clone = src;
                // If the value of an item is a pure object or array and DEEP is true, we turn on DEEP merge
                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;
    };

    // Implement "array/pure object, other types of value, direct shallow clone can be" shallow clone
    const clone = function clone(deep, obj, cache) {
        const type, Ctor, copy;
        if (typeofdeep ! = ="boolean") {
            obj = deep;
            deep = false;
        }

        // Dead recursion optimization
        !Array.isArray(cache) ? cache = [] : null;
        if (cache.indexOf(obj) > -1) return obj;
        cache.push(obj);

        // The original value type is returned directly
        if (obj == null) return obj;
        if (!/^(object|function)$/.test(typeof obj)) return obj;

        // Handle special values
        type = toType(obj);
        Ctor = obj.constructor;
        if (/^(number|string|boolean|regexp|date)$/.test(type)) return new Ctor(obj);
        if (type === "error") return new Ctor(obj.message);
        if (type === "function") {
            return function proxy() {
                const params = [].slice.call(arguments);
                return obj.apply(this, params);
            };
        }
        if(! isPlainObject(obj) && !Array.isArray(obj)) return obj;

        // Pure objects and arrays
        copy = new Ctor();
        each(obj, function (value, key) {
            if (deep) {
                / / copy
                copy[key] = clone(deep, value, cache);
                return;
            }
            / / shallow copy
            copy[key] = value;
        });
        return copy;
    };

    /* Expose API */
    const utils = {
        toType,
        isFunction,
        isWindow,
        isArrayLike,
        isPlainObject,
        isEmptyObject,
        isNumeric,
        each,
        merge,
        clone,
    };

    // Transfer the right of use
    const _$ = window. _; utils.noConflict =function noConflict() {
        if (window._ === utils) window. _ = _ $;return utils;
    };

    if (typeof module= = ="object" && typeof module.exports === "object") {
        module.exports = utils;
    }
    if (typeof window! = ="undefined") {
        window. _ =window.utils = utils;
    }
})();
Copy the code