util/baseUtil.js

/*
 * @Author: zhangyujie
 * @Date:   2016-05-11 14:48:57
 * @Last Modified by:   wxq
 * @Last Modified time: 2017-09-28 15:21:01
 * @Email: zhangyujie3344521@163.com
 * @File Path: F:\work\hyMap\src\util\baseUtil.js
 * @File Name: baseUtil.js
 * @Descript: 提供最基本的数据类型判断、、、可以使用underscore lodash 、、、、
 * @module baseUtil
 */
var hat = require('hat');

'use strict';
/**
 * baseUtil
 * @private
 * @module  baseUtil
 */
// 用于处理merge时无法遍历Date等对象的问题
var BUILTIN_OBJECT = {
    '[object Function]': 1,
    '[object RegExp]': 1,
    '[object Date]': 1,
    '[object Error]': 1
};

var objToString = Object.prototype.toString;
var arrayProto = Array.prototype;
var nativeForEach = arrayProto.forEach;
var nativeFilter = arrayProto.filter;
var nativeSlice = arrayProto.slice;
var nativeMap = arrayProto.map;
var nativeReduce = arrayProto.reduce;

/**
 * @param {object} source
 * @return {object} 拷贝后的新对象
 */
function clone(source) {

    if (typeof source == 'object' && source !== null) {

        var result = source;
        if (source instanceof Array) {

            result = [];
            for (var i = 0, len = source.length; i < len; i++) {

                result[i] = clone(source[i]);

            }

        } else if (!isDom(source)) {

            result = {};
            for (var key in source) {

                if (source.hasOwnProperty(key)) {

                    result[key] = clone(source[key]);

                }

            }

        }

        return result;

    }

    return source;

}

/**
 * @param {*} target
 * @param {*} source
 * @param {boolean} [overwrite=false]
 */
function merge(target, source, overwrite) {

    // We should escapse that source is string
    // and enter for ... in ...
    if (!isObject(source) || !isObject(target)) {

        return overwrite ? clone(source) : target;

    }

    for (var key in source) {

        if (source.hasOwnProperty(key)) {

            var targetProp = target[key];
            var sourceProp = source[key];

            if (isObject(sourceProp) && isObject(targetProp) && !isArray(sourceProp) && !isArray(targetProp) && !isDom(sourceProp) && !isDom(targetProp)) {

                // 如果需要递归覆盖,就递归调用merge
                merge(targetProp, sourceProp, overwrite);

            } else if (overwrite || !(key in target)) {

                // 否则只处理overwrite为true,或者在目标对象中没有此属性的情况
                // NOTE,在 target[key] 不存在的时候也是直接覆盖
                target[key] = clone(source[key], true);

            }

        }

    }
    return target;

}

/**
 * @param {Array} targetAndSources The first item is target, and the rests are source.
 * @param {boolean} [overwrite=false]
 * @return {*} target
 */
function mergeAll(targetAndSources, overwrite) {

    var result = targetAndSources[0];
    for (var i = 1, len = targetAndSources.length; i < len; i++) {

        result = merge(result, targetAndSources[i], overwrite);

    }
    return result;

}

/**
 * @param {*} target
 * @param {*} source
 */
function extend(target, source) {

    for (var key in source) {

        if (source.hasOwnProperty(key)) {

            target[key] = source[key];

        }

    }
    return target;

}

/**
 * @param {*} target
 * @param {*} source
 * @param {boolen} [overlay=false]
 */
function defaults(target, source, overlay) {

    for (var key in source) {

        if (source.hasOwnProperty(key) && (overlay ? source[key] != null : target[key] == null)) {

            target[key] = source[key];

        }

    }
    return target;

}

function createCanvas() {

    return document.createElement('canvas');

}

// FIXME
var _ctx;

function getContext() {

    if (!_ctx) {

        // Use util.createCanvas instead of createCanvas
        // because createCanvas may be overwritten in different environment
        _ctx = util.createCanvas().getContext('2d');

    }
    return _ctx;

}

/**
 * 查询数组中元素的index
 */
function indexOf(array, value) {

    if (array) {

        if (array.indexOf) {

            return array.indexOf(value);

        }
        for (var i = 0, len = array.length; i < len; i++) {

            if (array[i] === value) {

                return i;

            }

        }

    }
    return -1;

}

/**
 * 构造类继承关系
 *
 * @param {Function} clazz 源类
 * @param {Function} baseClazz 基类
 */
function inherits(clazz, baseClazz) {

    var clazzPrototype = clazz.prototype;

    function F() {
    }

    F.prototype = baseClazz.prototype;
    clazz.prototype = new F();

    for (var prop in clazzPrototype) {

        clazz.prototype[prop] = clazzPrototype[prop];

    }
    clazz.prototype.constructor = clazz;
    clazz.superClass = baseClazz;

}

/**
 * @param {Object|Function} target
 * @param {Object|Function} sorce
 * @param {boolean} overlay
 */
function mixin(target, source, overlay) {

    target = 'prototype' in target ? target.prototype : target;
    source = 'prototype' in source ? source.prototype : source;

    defaults(target, source, overlay);

}

/**
 * @param {Array|TypedArray} data
 */
function isArrayLike(data) {

    if (!data) {

        return;

    }
    if (typeof data == 'string') {

        return false;

    }
    return typeof data.length == 'number';

}

/**
 * 数组或对象遍历
 * @memberOf module:zrender/tool/util
 * @param {Object|Array} obj
 * @param {Function} cb
 * @param {*} [context]
 */
function each(obj, cb, context) {

    if (!(obj && cb)) {

        return;

    }
    if (obj.forEach && obj.forEach === nativeForEach) {

        obj.forEach(cb, context);

    } else if (obj.length === +obj.length) {

        for (var i = 0, len = obj.length; i < len; i++) {

            cb.call(context, obj[i], i, obj);

        }

    } else {

        for (var key in obj) {

            if (obj.hasOwnProperty(key)) {

                cb.call(context, obj[key], key, obj);

            }

        }

    }

}

/**
 * 数组映射
 * @memberOf module:zrender/tool/util
 * @param {Array} obj
 * @param {Function} cb
 * @param {*} [context]
 * @return {Array}
 */
function map(obj, cb, context) {

    if (!(obj && cb)) {

        return;

    }
    if (obj.map && obj.map === nativeMap) {

        return obj.map(cb, context);

    } else {

        var result = [];
        for (var i = 0, len = obj.length; i < len; i++) {

            result.push(cb.call(context, obj[i], i, obj));

        }
        return result;

    }

}

/**
 * @memberOf module:zrender/tool/util
 * @param {Array} obj
 * @param {Function} cb
 * @param {Object} [memo]
 * @param {*} [context]
 * @return {Array}
 */
function reduce(obj, cb, memo, context) {

    if (!(obj && cb)) {

        return;

    }
    if (obj.reduce && obj.reduce === nativeReduce) {

        return obj.reduce(cb, memo, context);

    } else {

        for (var i = 0, len = obj.length; i < len; i++) {

            memo = cb.call(context, memo, obj[i], i, obj);

        }
        return memo;

    }

}

/**
 * 数组过滤
 * @memberOf module:zrender/tool/util
 * @param {Array} obj
 * @param {Function} cb
 * @param {*} [context]
 * @return {Array}
 */
function filter(obj, cb, context) {

    if (!(obj && cb)) {

        return;

    }
    if (obj.filter && obj.filter === nativeFilter) {

        return obj.filter(cb, context);

    } else {

        var result = [];
        for (var i = 0, len = obj.length; i < len; i++) {

            if (cb.call(context, obj[i], i, obj)) {

                result.push(obj[i]);

            }

        }
        return result;

    }

}

/**
 * 数组项查找
 * @memberOf module:zrender/tool/util
 * @param {Array} obj
 * @param {Function} cb
 * @param {*} [context]
 * @return {Array}
 */
function find(obj, cb, context) {

    if (!(obj && cb)) {

        return;

    }
    for (var i = 0, len = obj.length; i < len; i++) {

        if (cb.call(context, obj[i], i, obj)) {

            return obj[i];

        }

    }

}

/**
 * @memberOf module:zrender/tool/util
 * @param {Function} func
 * @param {*} context
 * @return {Function}
 */
function bind(func, context) {

    var args = nativeSlice.call(arguments, 2);
    return function () {

        return func.apply(context, args.concat(nativeSlice.call(arguments)));

    };

}

/**
 * @memberOf module:zrender/tool/util
 * @param {Function} func
 * @return {Function}
 */
function curry(func) {

    var args = nativeSlice.call(arguments, 1);
    return function () {

        return func.apply(this, args.concat(nativeSlice.call(arguments)));

    };

}

/**
 * @memberOf module:zrender/tool/util
 * @param {*} value
 * @return {boolean}
 */
function isArray(value) {

    return objToString.call(value) === '[object Array]';

}

/**
 * @memberOf module:zrender/tool/util
 * @param {*} value
 * @return {boolean}
 */
function isFunction(value) {

    return typeof value === 'function';

}

/**
 * @memberOf module:zrender/tool/util
 * @param {*} value
 * @return {boolean}
 */
function isString(value) {

    return objToString.call(value) === '[object String]';

}

/**
 * @memberOf module:zrender/tool/util
 * @param {*} value
 * @return {boolean}
 */
function isObject(value) {

    // Avoid a V8 JIT bug in Chrome 19-20.
    // See https://code.google.com/p/v8/issues/detail?id=2291 for more details.
    var type = typeof value;
    return type === 'function' || (!!value && type == 'object');

}

/**
 * @memberOf module:zrender/tool/util
 * @param {*} value
 * @return {boolean}
 */
function isDom(value) {

    return value && value.nodeType === 1 && typeof(value.nodeName) == 'string';

}

/**
 * If value1 is not null, then return value1, otherwise judget rest of values.
 * @param  {...*} values
 * @return {*} Final value
 */
function retrieve(values) {

    for (var i = 0, len = arguments.length; i < len; i++) {

        if (arguments[i] != null) {

            return arguments[i];

        }

    }

}

/**
 * @memberOf module:zrender/tool/util
 * @param {Array} arr
 * @param {number} startIndex
 * @param {number} endIndex
 * @return {Array}
 */
function slice() {

    return Function.call.apply(nativeSlice, arguments);

}

/**
 * @param {boolean} condition
 * @param {string} message
 */
function assert(condition, message) {

    if (!condition) {

        throw new Error(message);

    }

}

function transObject(arr) {

    if (typeof arr === 'object' && !(arr instanceof Array)) {

        var hasProp = false;
        for (var prop in arr) {

            hasProp = true;
            break;
        
        }
        if (hasProp) {

            return [arr];
        
        } else {

            console.info('arr is empty object');
            return false;
        
        }
    
    }

}

function isEmpty(value) {

    return (Array.isArray(value) && value.length === 0)
        || (Object.prototype.isPrototypeOf(value) && Object.keys(value).length === 0);

}

function guid(){

    return  hat();

}

const baseUtil = {
    inherits: inherits,
    mixin: mixin,
    clone: clone,
    merge: merge,
    mergeAll: mergeAll,
    extend: extend,
    defaults: defaults,
    getContext: getContext,
    createCanvas: createCanvas,
    indexOf: indexOf,
    slice: slice,
    find: find,
    isArrayLike: isArrayLike,
    each: each,
    map: map,
    reduce: reduce,
    filter: filter,
    bind: bind,
    curry: curry,
    isArray: isArray,
    isString: isString,
    isObject: isObject,
    isFunction: isFunction,
    isDom: isDom,
    retrieve: retrieve,
    assert: assert,
    transObject: transObject,
    isEmpty:isEmpty,
    guid:guid
};

export default baseUtil;