结论:对象的拷贝不能采用直接赋值的方式。
背景
踩过的坑如下:
formData本来是父组件传过来的,但是我不想直接用,于是我直接赋值给一个formDataCopy的对象。
但是诡异的事情发生了,就是在我填写自己的表单组件的时候,一旦表单的数据发生的变化时,本来是formDataCopy的值发生变化,但是‘formDataDefault值’ 这个字符串却被打印出来,也就是说formData改变了。
奇怪,formData是父组件传过来的值怎么会改变呢?
经过一番挣扎,才发现formDataCopy使用的是简单的赋值,导致formDataCopy和formData指向相同的对象。
formDataCopy一改变,formData就会跟着变。
以上是背景,所以我就对浅拷贝和深拷贝进行了总结:
浅拷贝
什么是浅拷贝:两者是指向一个对象。
对象的浅拷贝
1、对象的直接遍历赋值。
2、ES6中的 var copyObj = Object.assign({}, obj);
3、ES7扩展运算符 var copyObj = { ...obj }
4、Jquery浅拷贝 var copiedObject = jQuery.extend({}, originalObject)
如果改变了originalObject,copiedObject 也会变。
数组的浅拷贝
(两者指向不同的对象,但是只能拷贝一层)
- array.concat();
- array.slice(0);
如果该元素是个对象引用 (不是实际的对象),slice 会拷贝这个对象引用到新的数组里。两个对象引用都引用了同一个对象。如果被引用的对象发生改变,则新的和原来的数组中的这个元素也会发生改变,所以是浅拷贝。
对于字符串、数字及布尔值来说(不是 String、Number 或者 Boolean 对象),slice 会拷贝这些值到新的数组里。在别的数组里修改这些字符串或数字或是布尔值,将不会影响另一个数组。
也就是说,如果原数组改变的是基本数据类型,比如String,Boolean,Number的数据,不会影响到新数组;
但是如果改变的是对象或者数组中的数据,是会影响到新数组的,也也就是对于对象或者数组,新旧数组指向的是一个对象。
深拷贝
(下面说的深拷贝是基本对象的深拷贝,不考虑对象的复杂属性,比如set,get,Function等)
1、最简单的方式 JSON.parse(JSON.stringify(Obj))
这种方法使用较为简单,可以满足基本的深拷贝需求,而且能够处理JSON格式能表示的所有数据类型,但是对于正则表达式类型、函数类型等无法进行深拷贝(而且会直接丢失相应的值)。
2、jQuery深拷贝 var copiedObject = $.extend(true, {}, originalObject)
3、手动写递归方式
var array = [
{ number: 1 },
{ number: 2 },
{ number: 3 }
];
function copy (obj) {
var newobj = obj.constructor === Array ? [] : {};
if(typeof obj !== 'object'){
return;
}
for(var i in obj){
newobj[i] = typeof obj[i] === 'object' ? copy(obj[i]) : obj[i];
}
return newobj
}
var copyArray = copy(array)
copyArray[0].number = 100;
console.log(array); // [{number: 1}, { number: 2 }, { number: 3 }]
console.log(copyArray); // [{number: 100}, { number: 2 }, { number: 3 }]