我理解的浅拷贝和深拷贝

浅拷贝:通过一个对象来创建一个新对象,如果对象的属性的值是基本数据类型,直接把值赋给新对象,如果值是引用类型则把数据的对应的内存地址赋给新对象;因为两个对象对应的是同一个内存地址,当其中一个对象数据改变了,就相当于改变了内存地址,所以会影响到别一个对象;

 

深拷贝:通过一个对象来创建一个新对象,无论基本类型还是引用类型都将值赋给新对象,引用类型会开辟一块新的内存地址用于存放数据,因为不会相互影响;

 

 浅拷贝的方法:

 

 1.直接赋值:

 var obj1 = {a: 1, b: 2};

 var obj2 = obj1;

 obj2.b = 22;

 console.log(obj1.b); // 22

 

 2.Object.assign

var obj1 = {a: 1, b: 2};
var obj2 = {c: 3};
Object.assign(obj1, obj2);
console.log(obj1) // {a: 1, b: 2, c: 3}
obj2.c = 4;
console.log(obj1.c) // 3

乍一看,这貌似两个对象互不影响了!难道是深拷贝了?

var obj1 = {a: 1, b: 2};
var obj2 = {c: {z: 3}};
Object.assign(obj1, obj2);
console.log(obj1); // {a: 1, b: 2, c: {z: 3}}
console.log(obj2); // {c: {z: 3}}
obj2.c.z = 4;
console.log(obj1.c.z); // 4;

由些可见,Object.assign只是对第一层数据进行了深拷贝,实际上依然是一个浅拷贝。

 

 3.通过对象扩展运算符 ({ ...obj })

var obj1 = {
    a: 1,
    b: 2,
    c: {
        z: 3
    }
};
var obj2 = {...obj1};
console.log(obj2); // { a: 1, b: 2, c: { z: 3 } }

obj1.a = 11;
console.log(obj2.a);  // 1

obj1.c.z = 33;
console.log(obj2.c.z); // 33

由此可见...扩展运算符跟assign一样也是浅拷贝。

 

 4.通过数组的slice方法 

 

var arr1 = [1, [2, 3], 4];
var arr2 = arr1.slice();

console.log(arr2); // [1, [2, 3], 4]

arr1[0] = 11;
console.log(arr2[0]); // 1; 貌似深拷贝了,但是:

arr1[1][0] = 22;
console.log(arr2); // [1, [22, 3], 4;  还是太年轻啊!!!

 

 5.通过数组的concat方法。

 

var arr1 = [1, [2, 3], 4];
var arr2 = arr1.concat();

console.log(arr2); //  [1, [2, 3], 4]

arr1[0] = 11;
console.log(arr2[0]); // 1;

arr1[1][0] = 22; 
console.log(arr2[1][0]); // 22

 

深拷贝的方法:

1. JSON.stringify() + JSON.parse();

原理是:把一个对象序列化成为一个JSON字符串,将对象的内容转换成字符串的形式再保存在磁盘上,再用JSON.parse()反序列化将JSON字符串变成一个新的对象;

var obj1 = {
    a: 1,
    b: 2,
    c: {
        z: 3
    }
};

var obj2 = JSON.parse(JSON.stringify(obj1));
console.log(obj2); // {a: 1, b: 2, c: {z: 3}}
obj1.c.z = 33; 
console.log(obj2.c.z);
// 3

由此可见,已经实现了深拷贝!

但是,JSON.stringify()有个问题:无法拷贝函数、undefined、Symbol值,因为其在序列化时会忽略这些值;

var obj1 = {
    a: 1,
    b: 'b',
    c: [1,2,3],
    u: undefined,
    s: Symbol('s'),
    f: function() {
        console.log(123);
    }
};

var obj2 = JSON.parse(JSON.stringify(obj1));

console.log(obj2); // {a: 1, b: 'b', c: [1, 2, 3]}

2.递归:

var deepCopy = function(obj) {
    if (typeof obj !== 'object') return;
    var newObj = obj instanceof Array ? [] : {};
    for (var key in obj) {
        if (obj.hasOwnProperty(key)) {
            newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key];
        }
    }
    return newObj;
}

var obj1 = {
    a: 1,
    b: 'b',
    c: [1,2,3],
    u: undefined,
    s: Symbol('s'),
    f: function() {
        console.log(123);
    }
};

var obj2 = deepCopy(obj1)

 

 

哇~ 完美!!! 感觉得到了整个世界呢!

但是:

let y = {
  a: "z",
  [Symbol("z")]: "d"
};

let newY = deepCopy(y);

console.log(newY) // --> {a: "z"}
[Symbol("z")]: "d" 呢?

原来 for..in 是无法获取Symbol作为健名的元素的! 那该怎么办呢? 我们可以利用
Reflect.ownKeys()来得到包括Symbol在内所有实例的属性,从而可以实现Symbol属性的深拷贝!!!
/**
 * S.工具函数
 * */
// 判断是否是对象(函数和object,不包括null)
function isObject(obj) {
    var type = typeof obj;
    return type === 'function' || type === 'object' && !!obj;
}
// Internal function for creating a toString-based type tester. (underscore判断数据类型[object Array] 和 [object Object])- tagTester('Object')
function tagTester(name) {
    return function(obj) {
        return toString.call(obj) === '[object ' + name + ']';
    };
}
// 判断是否为数据(原生支持)
let isArray = Array.isArray;
/**
 * E.工具函数
 * */
// 深拷贝
function deepClone(obj) {
    let objIsObject = tagTester('Object')(obj);
    let objIsArray = isArray(obj);
    if (!objIsObject && !objIsArray) return;
    let newObj = objIsArray ? [] : {};
    if (objIsArray) {
        // 如果是数组...
        return obj.map((item) => {
            return (tagTester('Object')(item) || isArray(item)) ? deepClone(item) : item;
        })
    }
    if (objIsObject) {
        // 获取对象的所以key(包括symbol的key)
        let ownKeys = Reflect.ownKeys(obj);
        for (let i = 0, Len = ownKeys.length; i < Len; i++) {
            let key = ownKeys[i];
            let curObj = obj[key];
            if (tagTester('Object')(curObj) || isArray(curObj)) {
                newObj[key] = deepClone(curObj);
            } else {
                newObj[key] = curObj
            }
        }
    }
    return newObj;
}

// test
let obj = {
    num: 1,
    str: 'string',
    nu: null,
    un: undefined,
    fn: function(a) { 
        a += 1;
        return a;
    },
    ob: {
        x: 1,
        y: 2,
        z: {
            a: 'a',
            b: 'b'
        }
    },
    [Symbol('foo')]: 'i am symbol',
    arr: [1, 2, 3, [4, 5], {m: 9, n: 8}, function() {return 1}]
};
var newObj = deepClone(obj);

 

 

   这样一来就真的得到了整个世界了!

 

posted @ 2019-12-04 23:24  沐浴点阳光  阅读(287)  评论(0编辑  收藏  举报