1172 字
6 分钟
深拷贝与浅拷贝
深拷贝与浅拷贝
一下部分参照知乎中的提问: javascript中的深拷贝和浅拷贝
浅拷贝
赋值(=)和浅拷贝的区别
那么赋值和浅拷贝有什么区别呢,我们看下面这个例子:
var obj1 = { 'name' : 'zhangsan', 'age' : '18', 'language' : [1,[2,3],[4,5]],};
var obj2 = obj1;
var obj3 = shallowCopy(obj1);function shallowCopy(src) { var dst = {}; for (var prop in src) { if (src.hasOwnProperty(prop)) { dst[prop] = src[prop]; } } return dst;}
obj2.name = "lisi";obj3.age = "20";
obj2.language[1] = ["二","三"];obj3.language[2] = ["四","五"];
console.log(obj1);//obj1 = {// 'name' : 'lisi',// 'age' : '18',// 'language' : [1,["二","三"],["四","五"]],//};
console.log(obj2);//obj2 = {// 'name' : 'lisi',// 'age' : '18',// 'language' : [1,["二","三"],["四","五"]],//};
console.log(obj3);//obj3 = {// 'name' : 'zhangsan',// 'age' : '20',// 'language' : [1,["二","三"],["四","五"]],//};先定义个一个原始的对象 obj1,然后使用赋值得到第二个对象 obj2,然后通过浅拷贝,将 obj1 里面的属性都赋值到 obj3 中。也就是说:
obj1:原始数据obj2:赋值操作得到obj3:浅拷贝得到
然后我们改变 obj2 的 name 属性和 obj3 的 name 属性,可以看到,改变赋值得到的对象 obj2 同时也会改变原始值 obj1,而改变浅拷贝得到的的 obj3 则不会改变原始对象 obj1。这就可以说明赋值得到的对象 obj2 只是将指针改变,其引用的仍然是同一个对象,而浅拷贝得到的的 obj3 则是重新创建了新对象。
然而,我们接下来来看一下改变引用类型会是什么情况呢,我又改变了赋值得到的对象 obj2 和浅拷贝得到的 obj3 中的 language 属性的第二个值和第三个值(language 是一个数组,也就是引用类型)。结果见输出,可以看出来,无论是修改赋值得到的对象 obj2 和浅拷贝得到的 obj3 都会改变原始数据。
这是因为浅拷贝只复制一层对象的属性,并不包括对象里面的为引用类型的数据。所以就会出现改变浅拷贝得到的 obj3 中的引用类型时,会使原始数据得到改变。
深拷贝:将 B 对象拷贝到 A 对象中,包括 B 里面的子对象,
浅拷贝:将 B 对象拷贝到 A 对象中,但不包括 B 里面的子对象
| — | 和原数据是否指向同一对象 | 第一层数据为基本数据类型 | 原数据中包含子对象 |
|---|---|---|---|
| 赋值 | 是 | 改变会使原数据一同改变 | 改变会使原数据一同改变 |
| 浅拷贝 | 否 | 改变不会使原数据一同改变 | 改变会使原数据一同改变 |
| 深拷贝 | 否 | 改变不会使原数据一同改变 | 改变不会使原数据一同改变 |
深拷贝
看了这么半天,你也应该清楚什么是深拷贝了吧,如果还不清楚,我就剖腹自尽(ಥ_ಥ)
深拷贝是对对象以及对象的所有子对象进行拷贝。
那么问题来了,怎么进行深拷贝呢?
思路就是递归调用刚刚的浅拷贝,把所有属于对象的属性类型都遍历赋给另一个对象即可。我们直接来看一下 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}利用weakMap实现递归clone
function clone(target, weakMap = new weakMap) { if ((typeof target === 'object')) { let cloneTarget = Array.isArray.call(target) ? [] : {}; if (weakMap.get(target)) { return weakMap.get(target); } weakMap.set(target, cloneTarget); for (const key in target) { cloneTarget[key] = clone(target[key], weakMap); } return cloneTarget; } else { return target; }}扩展运算符(…) 是什么类型的拷贝?
浅拷贝