浅拷贝和深拷贝都只针对于引用数据类型,
基本数据类型拷贝的直接是变量的值。
首先举个栗子:
var m = {a: 10, b: 20}
var n = m;
n.a = 15;
m;
// {a: 15, b: 20}
通过等号赋值的方式是浅拷贝,拷贝的是对象的引用,var n = m, 是将对象m的引用赋给了a,这时 n 和 m 指向了同一块内存地址,n 修改 a 的值也就修改了同一块内存地址,当然a的值也就变了。
var m = { a: 10, b: 20 }
var n = { a: m.a, b: m.b }
n.a = 15
此时,再输出m的值为:
m中a的值没有变;
而对象n中a的值为15,没有改变m对象
浅拷贝只复制指向某个对象的引用,而不复制对象本身,新旧对象还是共享同一块内存
但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象;
也就是说深拷贝会为这个对象重新开辟一块内存,来存放这个对象,和之前对象没有关系,修改这个对象也不会影响之前的对象。
深拷贝和浅拷贝最根本的区别:
在于是否是真正获取了一个对象的复制实体,而不是引用,浅拷贝只复制对象的第一层属性
深拷贝在计算机中开辟了一块内存地址用于存放复制的对象,而浅拷贝仅仅是指向被拷贝的内存地址,如果原地址中对象被改变了,那么浅拷贝出来的对象也会相应改变。深拷贝可以对对象的属性进行递归复制
深拷贝的实现方法:
1、最简单的使用JSON.stringify和JSON.parse实现深拷贝:
JSON.stringify把对象转成字符串,再用JSON.parse把字符串转成新的对象;
function deepCopy(object) {
return JSON.parse(JSON.stringify(object))
}
缺陷:它会抛弃对象的constructor,深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object;
而且这种方法能正确处理的对象只有 Number, String, Boolean, Array, 扁平对象,也就是说,只有可以转成JSON格式的对象才可以这样用,像null, undefined, function 这些办法转成JSON
2.对象只有一层的话可以使用Object.assign(target, source)
3.递归拷贝
function deepClone(initalObj, finalObj) {
var obj = finalObj || {};
for (var i in initalObj) {
var prop = initalObj[i];
if(prop === obj) {
continue;
}
if (typeof prop === 'object') {
obj[i] = (prop.constructor === Array) ? [] : {};
arguments.callee(prop, obj[i]);
} else {
obj[i] = prop;
}
}
return obj;
}
var str = {};
var obj = { a: {a: "hello", b: 21} };
deepClone(obj, str);
console.log(str.a);
4、使用Object.create()方法
直接使用var newObj = Object.create(oldObj),可以达到深拷贝的效果。
var obj = {a: 10, b: 'hi', c: 20};
var newObj = Object.create(obj);
newObj.__proto__.b = 'hello';
obj;
{a: 10, b: "hello", c: 20}
5、jquery实现深拷贝,jquery 提供一个$.extend可以用来做深拷贝;
var $ = require('jquery');
var obj1 = {
a: 3,
b: { f: { g: 1 } },
c: [1, 2, 3]
};
var obj2 = $.extend(true, {}, obj1);
console.log(obj1.b.f === obj2.b.f);
// false
6.使用函数库lodash,也有提供_.cloneDeep用来做 Deep Copy
var _ = require('lodash');
var obj1 = {
a: 1,
b: { f: { g: 1 } },
c: [1, 2, 3]
};
var obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f);
// false
浅拷贝的方法:
1.当然也可以通过简单的等号=赋值来实现浅拷贝,我们也可以封装一个函数来实现
2.可以通过slice()方法实现
3.Object.assign(target, source)实现
Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。
但是 Object.assign() 进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。
var obj1 = { a: {b: "hello", c: 30} };
var sourceObj = Object.assign({}, obj1);
sourceObj.a.b = "Hi";
可以看到以上obj1.a.b = ‘Hi’,值改变了,所以是浅拷贝。
注意:当object只有一层的时候,是深拷贝,例如如下:
var obj1 = { a: 10, b: 20, c: 30 };
var obj2 = Object.assign({}, obj1);
obj2.b = 50;
obj1;
注意到obj1中b的值没有变,所以是独立copy了obj1的对象本身,而不是其引用