2016年12月20日 星期二

(js)deepCopy

module.exports = deepCopy;

/**
 * 可複製3種格式({}, [], Map)
 *
 * g8
 */
function deepCopy(data) {
    // debugger;

    var toString = Object.prototype.toString;
    var clone;
    var type = _getType(data);

    // 若(data)是(不含子孫物件)
    if (data == null || (type != 'array' && type != 'map' && !_isPlainObject(data))) {
        clone = data;
        return clone;
    }
    /* ---------------------------------- */
    // data含有子孫物件
    if (type == 'map') {
        clone = new Map();
    } else if (type == 'array') {
        clone = [];
    } else {
        clone = {};
    }

    copyChild(clone, data);
    //////////////////////////////////////////////////
    /**
     * 會進來的(data)一定是有子孫元素
     */
    function copyChild(target, data) {
        // debugger;
        var clone;
        var dataType = _getType(data);
        var tar_getType = _getType(target);
        /* ---------------------------------- */
        if (tar_getType != dataType) {
            throw new Error('要拷貝的數據型態不同');
        } else if (dataType == 'map') {
            // 若是(Map)
            data.forEach(function(child, key) {
                var childType = _getType(child);

                if (childType != 'array' && childType != 'map' && !_isPlainObject(child)) {
                    // 若子物件是單純數據
                    target.set(key, child);
                } else {
                    // 若(child)物件還攜帶有子孫

                    // (clone)必須與(child)同型態
                    clone = _getInitClone(childType);
                    /* ------------------------ */
                    // 遞回,把child拷貝到 clone
                    copyChild(clone, child);
                    target.set(key, clone);
                }
            });

        } else if (dataType == 'array') {
            // 若是(array)

            data.forEach(function(child, key) {
                var childType = _getType(child);

                if (childType != 'array' && childType != 'map' && !_isPlainObject(child)) {
                    // 若子物件是單純數據

                    target[key] = child;

                } else {
                    // 若子物件還攜帶有子孫

                    // (clone)必須與(child)同型態
                    clone = _getInitClone(childType);
                    /* ------------------------ */
                    // 遞回,把child拷貝到 clone
                    copyChild(clone, child);
                    target[key] = clone;
                }
            });

        } else if (_isPlainObject(data)) {
            // 若(data)是(PlainObject)
            for (var key in data) {
                if (data.hasOwnProperty(key)) {
                    var child = data[key];
                    var childType = _getType(child);

                    if (childType != 'array' && childType != 'map' && !_isPlainObject(child)) {
                        // 若子物件是單純數據

                        target[key] = child;

                    } else {
                        // 若子物件還攜帶有子孫

                        // (clone)必須與(child)同型態
                        clone = _getInitClone(childType);
                        /* ------------------------ */
                        // 遞回,把child拷貝到 clone
                        copyChild(clone, child);
                        target[key] = clone;
                    }
                }
            }
        } else {
            throw new Error('data have no child');
        }
    };
    /* ---------------------------------- */
    return clone;
    ////////////////////////////////////////////////////////////////////////////

    function _getInitClone(type) {
        var clone;
        switch (type) {
            case 'array':
                clone = [];
                break;
            case 'map':
                clone = new Map();
                break;
            default:
                clone = {};
                break;
        }
        return clone;
    };
    /* ====================================================================== */
    function _getType(obj) {
        var type = toString.call(obj) || '';
        type = type.replace(/(^\s?\[object\s?)|(\]\s?$)/gi, '').toLowerCase();
        return type;
    };
    /* ====================================================================== */
    /**
     * 只能是{}
     */
    function _isPlainObject(obj) {
        if (!obj || _getType(obj) !== "object") {
            return false;
        }

        var constructorName = '';
        try {
            constructorName = obj.constructor.toString();
        } catch (error) {}

        // 'Object() {[native code]}'
        if (!/function\s*Object\(\s*\)\s*\{\s*\[native\s*code\]\s*\}\s*$/gi.test(constructorName)) {
            return false;
        }
        return true;
    };
};