Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

深拷贝和浅拷贝 #39

Open
TieMuZhen opened this issue Nov 25, 2021 · 0 comments
Open

深拷贝和浅拷贝 #39

TieMuZhen opened this issue Nov 25, 2021 · 0 comments

Comments

@TieMuZhen
Copy link
Owner

TieMuZhen commented Nov 25, 2021

浅拷贝

浅拷贝是会将对象的每个属性进行依次复制,但是当对象的属性值是引用类型时,实质复制的是其引用,当引用指向的值改变时也会跟着变化。

可以使用for inObject.assign扩展运算符 ...Array.prototype.slice()Array.prototype.concat()进行浅拷贝,例如:

let obj = {
    name: 'Yvette',
    age: 18,
    hobbies: ['reading', 'photography']
}
let target = {};
let obj2 = Object.assign(target, obj);
let obj3 = {...obj};

obj.name = 'Jack';
obj.hobbies.push('coding');

console.log(obj); //{ name: 'Jack', age: 18,hobbies: [ 'reading', 'photography', 'coding' ] }
console.log(target);//{ name: 'Yvette', age: 18,hobbies: [ 'reading', 'photography', 'coding' ] }
console.log(obj2);//{ name: 'Yvette', age: 18,hobbies: [ 'reading', 'photography', 'coding' ] }
console.log(obj3);//{ name: 'Yvette', age: 18,hobbies: [ 'reading', 'photography', 'coding' ] }

可以看出浅拷贝只最第一层属性进行了拷贝,当第一层的属性值是基本数据类型时,新的对象和原对象互不影响,但是如果第一层的属性值是复杂数据类型,那么新对象和原对象的属性值其指向的是同一块内存地址。

深拷贝

深拷贝复制变量值,对于非基本类型的变量,则递归至基本类型变量后,再复制。 深拷贝后的对象与原来的对象是完全隔离的,互不影响,对一个对象的修改并不会影响另一个对象。

深拷贝实现

1、深拷贝最简单的实现是: JSON.parse(JSON.stringify(obj))

JSON.parse(JSON.stringify(obj))是最简单的实现方式,但是有一些缺陷:

  1. 对象的属性值是函数时,无法拷贝。
  2. 原型链上的属性无法拷贝
  3. 不能正确的处理 Date 类型的数据
  4. 不能处理 RegExp
  5. 会忽略 symbol
  6. 会忽略 undefined

1、如果json里面有时间对象,则序列化结果:时间对象=>字符串的形式;

let obj = {
    age: 18,
    date: new Date()
};
let objCopy = JSON.parse(JSON.stringify(obj));
console.log('obj', obj);
console.log('objCopy', objCopy);
console.log(typeof obj.date); // object
console.log(typeof objCopy.date); // string

2、如果json里有RegExp、Error对象,则序列化的结果将只得到空对象 RegExp、Error => {};

let obj = {
    age: 18,
    reg: new RegExp('\\w+'),
    err: new Error('error message')
};
let objCopy = JSON.parse(JSON.stringify(obj));
console.log('obj', obj);
console.log('objCopy', objCopy);

3、如果json里有 function,undefined,则序列化的结果会把 function,undefined 丢失;

let obj = {
    age: 18,
    fn: function () {
        console.log('fn');
    },
    hh: undefined
};
let objCopy = JSON.parse(JSON.stringify(obj));
console.log('obj', obj);
console.log('objCopy', objCopy);

4、如果json里有NaN、Infinity和-Infinity,则序列化的结果会变成null;

let obj = {
    age: 18,
    hh: NaN,
    isInfinite: 1.7976931348623157E+10308,
    minusInfinity: -1.7976931348623157E+10308
};
let objCopy = JSON.parse(JSON.stringify(obj));
console.log('obj', obj);
console.log('objCopy', objCopy);

5、如果json里有对象是由构造函数生成的,则序列化的结果会丢弃对象的 constructor;

function Person(name) {
    this.name = name;
}
let obj = {
    age: 18,
    p1: new Person('lxcan')
};
let objCopy = JSON.parse(JSON.stringify(obj));

console.log('obj', obj);
console.log('objCopy', objCopy);

console.log(obj.p1.__proto__.constructor === Person); // true
console.log(objCopy.p1.__proto__.constructor === Person); // false

console.log(objCopy.p1.__proto__.constructor === Object); // true

6、如果对象中存在循环引用的情况也无法实现深拷贝

let obj = {
    age: 18
};
obj.obj = obj;
let objCopy = JSON.parse(JSON.stringify(obj));
console.log('obj', obj);
console.log('objCopy', objCopy);

如果拷贝的对象不涉及上面的情况,可以使用JSON.parse(JSON.stringify(obj))实现深拷贝。

2、deepClone 实现深拷贝

  1. 如果是基本数据类型和function类型,直接返回
  2. 如果是RegExp或者Date类型,返回对应类型
  3. 如果是复杂数据类型,递归。
  4. 考虑循环引用的问题

拓展:
WeakMap 其键必须是对象,而值可以是任意的。其持有的每个键是“弱引用”,这意味着在没有其他引用存在时垃圾回收能正确进行,其持有的每个键只有在其没有被回收时才是有效的。

正由于这样的弱引用,WeakMap 的 key 是不可枚举的 (没有方法能给出所有的 key)。如果key 是可枚举的话,其列表将会受垃圾回收机制的影响,从而得到不确定的结果。因此,如果你想要这种类型对象的 key 值的列表,应该使用 Map。

function deepClone(obj, hash = new WeakMap()) { //递归拷贝
    if (obj instanceof RegExp) return new RegExp(obj);
    if (obj instanceof Date) return new Date(obj);
    // 如果是基本数据类型和'function'类型的话直接返回
    if (obj === null || typeof obj !== 'object') {
        return obj;
    }
    if (hash.has(obj)) {
        return hash.get(obj);
    }
    /**
     * 如果obj是数组,那么 obj.constructor 是 [Function: Array]
     * 如果obj是对象,那么 obj.constructor 是 [Function: Object]
     */
    let t = new obj.constructor();
    hash.set(obj, t);
    for (let key in obj) {
        //递归
        if (obj.hasOwnProperty(key)) {//是否是自身的属性
            t[key] = deepClone(obj[key], hash);
        }
    }
    return t;
}

测试代码

const test = { 
    a: {
        name: "hello", 
        age: 21
    },
    fn: function(){
        console.log("go");
    }
};
const res = deepClone(test);
res.a.age = 25;

console.log(test);
console.log(res);

// { a: { name: 'hello', age: 21 }, fn: [Function: fn] }
// { a: { name: 'hello', age: 25 }, fn: [Function: fn] }

参考文章

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant