JS的深浅拷贝

引用类型

之所以会出现深浅拷贝的问题,实质上是由于JS对基本类型和引用类型的处理不同。基本类型指的是简单的数据段,而引用类型指的是一个对象,而JS不允许我们直接操作内存中的地址,也就是不能操作对象的内存空间,所以,我们对对象的操作都只是在操作它的引用而已。

在复制时也是一样,如果我们复制一个基本类型的值时,会创建一个新值,并把它保存在新的变量的位置上。而如果我们复制一个引用类型时,同样会把变量中的值复制一份放到新的变量空间里,但此时复制的东西并不是对象本身,而是指向该对象的指针。所以我们复制引用类型后,两个变量其实指向同一个对象,改变其中一个对象,会影响到另外一个。

var num = 10;
var obj = {
    name: 'Nicholas'
}

var num2 = num;
var obj2 = obj;

obj.name = 'Lee';
obj2.name; // 'Lee'

浅拷贝

1.Object.assign()

其中第一个参数是我们最终复制的目标对象,后面的所有参数是我们的即将复制的源对象,支持对象或数组,一般调用的方式为

var newObj = Object.assign({}, originObj);

这样我们就得到了一个新的浅拷贝对象。另外[].slice()方法可以视为数组对象的浅拷贝。

2. 自定义浅拷贝

如果我们要复制对象的所有属性都不是引用类型时,就可以使用浅拷贝,实现方式就是遍历并复制,最后返回新的对象。

function shallowCopy(obj) {
    var copy = {};
    // 只复制可遍历的属性
    for (key in obj) {
        // 只复制本身拥有的属性
        if (obj.hasOwnProperty(key)) {
            copy[key] = obj[key];
        }
    }
    return copy;
}

如上面所说,我们使用浅拷贝会复制所有引用对象的指针,而不是具体的值,所以使用时一定要明确自己的需求,同时,浅拷贝的实现也是最简单的。

深拷贝

如果我们需要复制一个拥有所有属性和方法的新对象,就要用到深拷贝,JS并没有内置深拷贝方法,主要是因为:

  1. 深拷贝怎么定义?我们怎么处理原型?怎么区分可拷贝的对象?原生DOM/BOM对象怎么拷贝?函数是新建还是引用?这些edge case太多导致我们无法统一概念,造出大家都满意的深拷贝方法来。
  2. 内部循环引用怎么处理,是不是保存每个遍历过的对象列表,每次进行对比,然后再造一个循环引用来?这样带来的性能消耗可以接受吗。
var obj1 = {
    age: 20,
    name: 'xxx',
    address: {
        city: 'beijing'
    },
    arr: ['a', 'b', 'c']
}
var obj2 = deepClone(obj1)
obj2.address.city = '上海'

console.log(obj2)
console.log(obj1)

function deepClone(obj = {}) {
    if (typeof obj !== 'object' || obj == null) {
        return obj
    }
    let result;
    if (obj instanceof Array) {
        result = []
    } else {
        result = {}
    }
    for (const key in obj) {
        if (obj.hasOwnProperty(key)) {
            result[key] = deepClone(obj[key]);
        }
    }
    return result
}

你可能感兴趣的:(JS的深浅拷贝)