Source: lib/utils.js

'use strict';

/* 工具类 */
/** @module utils */

let class2type = {};
['Boolean', 'Number', 'String', 'Function', 'Array', 'Date', 'RegExp', 'Object', 'Error'].forEach(function (name) {
    class2type['[object ' + name + ']'] = name.toLowerCase();
});

/**
 * 获取参数的类型
 * @param {Object} obj - 要获取类型的参数
 * @return {String} 类型字符串, 返回的类型是: 'boolean', 'number', 'string', 'function', 'array', 'date', 'regExp', 'object', 'error'
 */
function type (obj) {
    return obj == null ? String(obj) : class2type[{}.toString.call(obj)] || 'object';
}

/**
 * 判断是不是函数类型
 * @param {Object} obj - 要获取类型的参数
 * @return {Boolean} 返回判断结果
 */
function isFunction(obj) {
    return typeof obj === 'function';
}

/**
 * isArray 判断是不是数组对象
 * @param {Object} obj - 要获取类型的参数
 * @return {Boolean} 返回判断结果
 */
function isArray(obj) {
    return obj instanceof Array;
}

/**
 * isObject 判断是不是Object对象
 * @param {Object} obj - 要获取类型的参数
 * @return {Boolean} 返回判断结果
 */
function  isObject(obj) {
    return type(obj) === 'object';
}

/**
 * isWindow 判断是不是window对象
 * @param {Object} obj - 要获取类型的参数
 * @return {Boolean} 返回判断结果
 */
function isWindow(obj) {
    return obj != null && obj === obj.window;
}

/**
 * isBrowserEnv 判断是不是浏览器环境
 * @return {Boolean} 返回判断结果
 */
function isBrowserEnv() {
    return typeof window !== 'undefined' && window.window === window;
}

/**
 * isNativeFunction 判断是不是Native函数(宿主对象定义的函数,比如document.querySelector)
 * @param {Object} fn - 要获取类型的参数
 * @return {Boolean} 返回判断结果
 */
function isNativeFunction(fn) {
    return (/\{\s*\[native code\]\s*\}/).test('' + fn);
}

/**
 * isPlainObject 判断是不是PlainObject对象
 * @param {Object} obj - 要获取类型的参数
 * @return {Boolean} 返回判断结果
 */
function isPlainObject(obj) {
    return isObject(obj) && !isWindow(obj) && Object.getPrototypeOf(obj) === Object.prototype;
}

/**
 * 判断手机号
 * @param {String|Number} mobile 手机号
 * @return {Boolean} 是否是手机号
 */
function isMobile(mobile) {
    return /^1[3456789][0-9]{9}$/.test(mobile);
}

/**
 * 遍历对象
 * @param {Object} elements - 要遍历的对象
 * @param {Function} callback - 回调函数,接收两个参数 index 和 value
 * @return {Boolean} hasOwnProperty 是否查找祖先级别原型链
 */
function each(elements, callback, hasOwnProperty = false) {
    if (!elements) {
        return this;
    }
    if (typeof elements.length === 'number') {
        [].every.call(elements, function (el, idx) {
            return callback.call(el, idx, el) !== false;
        });
    } else {
        for (let key in elements) {
              if (hasOwnProperty) {
                if (Object.prototype.hasOwnProperty.call(elements, key)) {
                    if (callback.call(elements[key], key, elements[key]) === false)
                        return elements;
                }
            } else {
                if (callback.call(elements[key], key, elements[key]) === false)
                    return elements;
            }
        }
    }
    return this;
}

/**
 * 拓展对象(模拟jquery的$.extend)
 * @param {Object} arg1 - 要遍历的dom数组
 * @param {Object} arg2
 * @param {Object} arg3
 * @param {Object} argn
 * @examples:
 * let newObj = extend(true, {} ,{a: 2}); // 深拷贝
 * let newObj2 = extend({} ,{a: 2},{a:4});
 */
function extend() { //from jquery2
    let options, name, src, copy, copyIsArray, clone,
        target = arguments[0] || {},
        i = 1,
        length = arguments.length,
        deep = false;

    if (typeof target === 'boolean') {
        deep = target;
        target = arguments[i] || {};
        i++;
    }

    if (typeof target !== 'object' && !isFunction(target)) {
        target = {};
    }

    if (i === length) {
        target = {};
        i--;
    }

    for (; i < length; i++) {
        if ((options = arguments[i]) != null) {
            for (name in options) {
                src = target[name];
                copy = options[name];
                if (target === copy) {
                    continue;
                }
                if (deep && copy && (isPlainObject(copy) || (copyIsArray = isArray(copy)))) {
                    if (copyIsArray) {
                        copyIsArray = false;
                        clone = src && isArray(src) ? src : [];
                    } else {
                        clone = src && isPlainObject(src) ? src : {};
                    }
                    target[name] = extend(deep, clone, copy);
                } else if (copy !== undefined) {
                    target[name] = copy;
                }
            }
        }
    }

    return target;
}

/**
 * 异步加载脚本,并执行
 * @param {String} url  - 脚本地址
 * @param {Function} callback - 加载成功的回调
 */
function loadScript(url, callback) {
    let script = document.createElement('script');
    script.type = 'text/javascript';
    if (typeof(callback) != 'undefined') {
        if (script.readyState) {
            script.onreadystatechange = function () {
                if (script.readyState == 'loaded' || script.readyState == 'complete') {
                    script.onreadystatechange = null;
                    callback();
                }
            };
        } else {
            script.onload = function () {
                callback();
            };
        }
    }
    script.src = url;
    document.body.appendChild(script);
}

/**
 * 节流函数
 * @param {Function} func  需要节流的函数
 * @param {Number} delay  间隔时间
 * @return {Function} callback  返回的心函数
 */
function throttle(func, delay) {
    let timer = null;
    return function() {
        let context = this;
        let args = arguments;
        if (!timer) {
            timer = setTimeout(function() {
                func.apply(context, args);
                timer = null;
            }, delay);
        }
    };
}

/**
 * 防抖函数
 * @param {Function} func  需要防抖的函数
 * @param {Number} wait  等待时间
 * @return {Function} callback  返回的心函数
 */
function debounce(func, wait) {
    let timeout = null;
    return function() {
        if(timeout !== null)
            clearTimeout(timeout);
        timeout = setTimeout(func, wait);
    };
}

export {
    type,
    isFunction,
    isArray,
    isObject,
    isWindow,
    isNativeFunction,
    isPlainObject,
    isMobile,
    isBrowserEnv,
    each,
    extend,
    loadScript,
    throttle,
    debounce
};