很深很深很深地对象、数组拷贝(基于ES6系列语法)(包括Symbol、get、set、原型)

344 阅读2分钟

直接上代码,验证过确实有效可行!
PS:
1、本人有一个自己积累的工具类,所以直接拷贝出来了,是用ES6的class语法写的。
2、代码中的this.isObject、this.isUndefined等等之类的,可自行实现,都是用于验证数据类型的,好实现,就不列出来了(最底下也已经列出来了)。
3、deepCopy方法需要deppCopyPrototype方法的协助,才能很深很深很深地拷贝对象的层层原型。

/**
     * 完整的对象、数组深度拷贝(包括Symbol、get、set、原型)<br />
     * PS: <br />
     * 1、处理被Vue处理过的对象、数组时,会报错!!!<br />
     * 2、this.isObject( obj ) || this.isArray( obj )为true才会执行!!!其他都会被原样返回!!!
     *
     * @param obj 对象、数组,必需
     *
     * @returns {Object|Array} 对象、数组,{}|[]
     */
    deepCopy( obj ){
        if( this.isArray( obj ) ){
            let newArr = [];

            obj.forEach( c => void ( newArr.push( typeof c !== 'object'
                                                  ? c
                                                  : this.deepCopy( c ) ) ) );

            return newArr;
        }
        else if( this.isObject( obj ) ){
            let val,
                newObj = 'constructor' in obj
                         ? new obj.constructor()
                         : {};

            for( let tmp of
                Reflect.ownKeys( obj ) ){
                val = obj[ tmp ];

                if( typeof val !== 'object' ){
                    Object.defineProperty( newObj, tmp, Object.getOwnPropertyDescriptor( obj, tmp ) );
                }
                else{
                    newObj[ tmp ] = this.deepCopy( val );
                }
            }

            let objPrototype = this.deppCopyPrototype( obj );

            !this.isNull( objPrototype ) && ( Object.setPrototypeOf( newObj, objPrototype ) );

            return newObj;
        }
        else{
            return obj;
        }
    }
/**
     * 深度拷贝一个对象的原型(包括原型的原型...N...),返回的是一个对象(已经深度拷贝的原型链)<br />
     * PS:<br />
     * 拿到这个方法的返回值后,直接使用Object.setPrototypeOf设置到目标对象就行!
     *
     * @param source 对象(具有原型的对象),必须
     *
     * @returns {null|Object} null|Objec
     */
    deppCopyPrototype( source ){
        let arr1 = [],
            sourcePrototype = Object.getPrototypeOf( source );

        while( sourcePrototype !== null ){
            arr1.push( this.deepCopy( sourcePrototype ) );
            sourcePrototype = Object.getPrototypeOf( sourcePrototype );
        }

        arr1.reverse();

        if( arr1.length === 0 ){
            return null;
        }
        else{
            for(
                let i = 0;
                i < arr1.length - 1;
                ++i
            ){
                Object.setPrototypeOf( arr1[ i + 1 ], arr1[ i ] );
            }

            return arr1[ arr1.length - 1 ];
        }
    }

PS:数据类型验证

/**
     * 判断数据是否为Array类型
     *
     * @param arg 数据,参数个数为1,必需
     *
     * @returns {Boolean} boolean,是true,否false
     */
    isArray( arg ){
        'use strict';

        return Array.isArray( arg );
    }

/**
     * 判断数据是否为Object类型<br />
     * PS:<br />
     * 1、包括'[object Module]'。
     *
     * @param arg 数据,参数个数为1,必需
     *
     * @returns {Boolean} boolean,是true,否false
     */
    isObject( arg ){
        return IsHandle1.call( this, arg, 'Object' ) || IsHandle1.call( this, arg, 'Module' );
    }

function IsHandle1( arg1, arg2 ){
    if( 'Element' === arg2 ){
        return this.dataT( arg1 )
                   .includes( arg2 );
    }

    let str1 = this.dataT( arg1 )
                   .split( ' ' )[ 1 ];

    return str1.slice( 0, str1.length - 1 ) === arg2;
}

/**
     * 获取数据类型<br />
     * PS:<br />
     * 1、如果传入的是被Proxy代理过的对象,会报错!!!
     *
     * @param arg 任何类型的数据,参数个数为1,必需
     *
     * @returns {String} string,数据类型的字符串,如'[object HTMLDocument]'
     */
    dataT( arg ){
        'use strict';

        return Object.prototype.toString.call( arg );
    }

/**
     * 判断数据是否为null值
     *
     * @param arg 数据,参数个数为1,必需
     *
     * @returns {Boolean} boolean,是true,否false
     */
    isNull( arg ){
        'use strict';

        return IsHandle1.call( this, arg, 'Null' );
    }