js对象克隆的几种方法

在js中对象克隆就是把对象值重新赋给一个新对象。克隆也存在深克隆于浅克隆的区别。引用类型的值都会被保存在堆内存中,在栈内存中会存在一个指针指向堆内存中的值。这时,如果只复制了指针,则可以说这个克隆为浅克隆,如果时根据指针找到具体的值,复制值,就可以称之为深克隆。

1、JSON

JSON多用于前后台的数据交互,后台传递字符串类型的JSON数据,前端通过JSON.parse() ,可以转化为一个json对象。
现有一个对象,可以通过JSON.stringify() 转为json字符串。字符串类型的值保存在栈内存当中,所以可以直接赋值达到深度克隆的结果。最后通过JSON.parse(),就可以实现深度克隆了。

var obj = {
    x: 1,
    y: {
        a: 1,
        b: 0,
        c: [1, 2, 3]
    }
};

// 直接等于号赋值,如果是基本类型的数据,这样足够达到深度克隆
var obj2 = obj;
console.log(obj2 == obj); //true 直接复制只是复制对象的指针,还指向同一个对象

// 转化为了字符串
var obj3 = JSON.parse(JSON.stringify(obj));
console.log(obj3 == obj) //false  通过json方法复制后的地址不一样
console.log(obj3); // 达到深度克隆

缺点:对象名如果是Symbol类型的是无法克隆的。

2、Object.create()

Object.create()用于把一个对象用于另一个对象的原型,这种以原型的方式来设置一个对象在某个方面也算的上是一种克隆,也可以间接的达到目的。但是不建议,我认为某些方面来说算不上克隆。

var obj = {
    x: 1,
    y: {
        a: 1,
        b: 0,
        c: [1, 2, 3]
    }
};

var obj2 = Object.create(obj);
console.log(obj2 == obj); //false
console.log(obj === obj2.__proto__); // true
console.log(obj2); // 输出{}

因为只是把对象设置为新对象的原型,所以新对象不会和原来的对象相等,但是原对象会与新对象的原型相等。一般来说对克隆出来的对象作修改,被克隆的对象没有被修改,就可以判定是一个深克隆出来的对象。但是要是去修改克隆出来的对象的原型中的属性,肯定会影响被克隆的对象,这样来看,又不像深克隆。所以是不是深克隆,界限不是很明确。不通过 _ _ proto_ _ 去访问原型上的对象进行修改也不会改变被克隆对象。

3、…运算符

都知道…运算符可以用于收集对象的所有属性和数组的所有值。

let arr = [1,2,3,4,5];
let arr2 = [...arr];
console.log(arr === arr2); // false 
console.log(arr2); // [1, 2, 3, 4, 5]

克隆出来的他俩不相等,那么这就可以判定这样是深克隆了吗。没错,这就可以这样判定,但是对象就不一定了。
如果对象的属性还是对象,那么就会出现意外,对象的属性都是基本类型的值的话,还是深度克隆。

let obj1 = {
    a : 1,
    b : '2',
    c : true,
    d : {
        x : 'd'
    }
}
let obj2 = {...obj1};
console.log(obj1 === obj2); // false

obj2.d.x = null; // 修改克隆出来的对象的属性d的属性x
console.log(obj1, obj2);

js对象克隆的几种方法_第1张图片
结果可以看到,obj1也被修改了。所以这不是深克隆。...运算符只能克隆一层对象,对于嵌套的对象是无法深克隆的。

4、assign

assign为ES7中的方法。方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
这个方法就是一个纯浅克隆。

let obj1 = {
    a : 1,
    b : '2',
    c : true,
    d : {
        x : 'd'
    }
}
let obj2 = Object.assign(obj1);
console.log(obj1 === obj2); // true
6、通用
Function.prototype.clone = function(obj, newObj = {}){// newObj是声明对象保存克隆对象

    // 对传进来的数据进行限制
    if(!(typeof obj == "function" || typeof obj == "object")){
        throw new Error(`obj must be a function or object`);
    }
    // 如果传进来的是个空对象或者空数组  则直接返回
    if(!(obj == undefined || obj == null)){
        if(Object.keys(obj).length == 0) return obj instanceof Array ? [] : {};
    } else {
        return;
    }
    

    // 遍历传进来的obj
    for (const key in obj) {
        // 判断当前属性是否是对象的属性(也就是不克隆原型上的属性)
        if (obj.hasOwnProperty(key)) { 
            const element = obj[key];

            // 判断当前属性值是引用数据类型还是基本数据类型
            if(typeof element == "function" || typeof element == "object"){

                // 进一步判断是那种引用类型然后进行赋值
                // 使用toString()
                let tostring = Object.prototype.toString;
                if(toString.call(obj[key]) == "[object Object]"){ // object类型
                    newObj[key] = new Object();
                } else if (tostring.call(obj[key]) == "[object RegExp]"){ // 正则类型
                    newObj[key] = new RegExp();
                } else if (tostring.call(obj[key]) == "[object Array]"){ // 数组类型
                    newObj[key] = new Array();
                }

                // 递归,当有数组或者对象的时候继续往里面深入,重复调用当前的函数
                Function.clone(obj[key], newObj[key]);

            } else {
                // 原始数据类型直接赋值即可
                newObj[key] = element;
            }

        }
    }

    return newObj;

}

let obj = {
    a : 1,
    1 : 'string',
    2 : true,
    b : null,
    c : undefined,
    innerobj : {
        innera : 1,
        inner1 : 'string',
        inner2 : true,
        innerb : null,
        innerc : undefined,
    },
    arr : [1,2,3,4,{arra : 1}],
    reg : new RegExp('a'),
    fun : function(){
        let funa = 1;
        let funarr = [1,2,3];
        let reg = /^a$/;
    }
}

你可能感兴趣的:(js对象)