深拷贝(deepCopy):增加了一个指针并申请了一个新的内存,新的指针指向新的内存地址。
浅拷贝(shallowCopy):只增加了一个新指针指向已存在的内存地址。浅拷贝只复制一层对象的属性。
栈(stack)为自动分配的内存空间,它由系统自动释放;而堆(heap)则是动态分配的内存,大小不定也不会自动释放。
结合栈和堆与数据类型拷贝的关系去理解:
1.基本类型--名,值,都存储在栈内存中,例如:let a = 1; [name:a;value:1]
2.当你复制 b = a;会开辟一个新的栈空间,存放b的名和值,这也就是说,修改b并不会影响到a
3.引用数据类型--名存在栈内存中,值存在于堆内存中,栈内存会提供一个引用的地址指向堆内存中的值;即在栈内存中原本存放value的值,变成了存放引用地址;
4.所以当引用类型进行 b = a;实际上拷贝的是栈里面的引用地址,而不是真正的值(在堆里面);修改b的时候,会影响到a,这就是浅拷贝
所以深拷贝实现,就是在堆中为b新开辟存放值的空间。
怎么实现深拷贝?怎么解决浅拷贝?
1.将一个对象转为json对象。然后再解析这个json对象
let obj = {a:{b:22}};
let copy = JSON.parse(JSON.stringify(obj));
2.lodash的cloneDeep
3.Vue.extend(),jq的$.extend( [deep ], target, object1 [, objectN ] )
4.递归去复制所有层级属性【或是循环,其实所有方式的原理,都是复制层级的属性值】
参考Zepto 中深拷贝的代码:
// 内部方法:用户合并一个或多个对象到第一个对象
// 参数:
// target 目标对象 对象都合并到target里
// source 合并对象
// deep 是否执行深度合并
function extend(target, source, deep) {
for (key in source)
if (deep && (isPlainObject(source[key]) || isArray(source[key]))) {
// source[key] 是对象,而 target[key] 不是对象, 则 target[key] = {} 初始化一下,否则递归会出错的
if (isPlainObject(source[key]) && !isPlainObject(target[key]))
target[key] = {}
// source[key] 是数组,而 target[key] 不是数组,则 target[key] = [] 初始化一下,否则递归会出错的
if (isArray(source[key]) && !isArray(target[key]))
target[key] = []
// 执行递归
extend(target[key], source[key], deep)
}
// 不满足以上条件,说明 source[key] 是一般的值类型,直接赋值给 target 就是了
else if (source[key] !== undefined) target[key] = source[key]
}
// Copy all but undefined properties from one or more
// objects to the `target` object.
$.extend = function(target){
var deep, args = slice.call(arguments, 1);
//第一个参数为boolean值时,表示是否深度合并
if (typeof target == 'boolean') {
deep = target;
//target取第二个参数
target = args.shift()
}
// 遍历后面的参数,都合并到target上
args.forEach(function(arg){ extend(target, arg, deep) })
return target
}