前言:为什么需要区分深浅拷贝?
深浅拷贝的定义(区别)
1.浅拷贝:将原对象或原数组的引用直接赋值给新对象、新数组,新对象/新数组只是原对象的一个引用。
2.深拷贝:创建一个新的对象和数组,将原对象的各项属性以及属性对应的值(数组的所有元素)拷贝过来,是‘值’而不是‘引用’。
为什么要使用深拷贝呢?
希望在操作改变新的对象/(或数组)的时候,不改变原对象(数组)。
场景:
handler(newVal,oldVal){
this.FormDataCopy = JSON.parse(JSON.stringify(newVal));
this.formDataDefault = {
name:newVal.name || '',
contact:newVal.contact || '',
level:newVal.level || ''
}
console.log('formDataDefault值:',this.formDataDefault);
}
deep:true
}
formData是父组件传来的,直接赋值给一个formDataCopy的对象。
但是诡异的事情发就是在填写表单组件的时候,一旦表单的数据发生的变化时,本来是formDataCopy的值发生变化,但是‘formDataDefault值’ 这个字符串却被打印出来,也就是说formData改变了。 奇怪,formData是父组件传过来的值怎么会改变呢?
经过一番挣扎,才发现formDataCopy使用的是简单的赋值,导致formDataCopy和formData指向相同的对象。 formDataCopy一改变,formData就会跟着变。 以上是背景,所以我就对浅拷贝和深拷贝进行了总结
深拷贝的要求程度
深拷贝通常要考虑,是仅‘深’拷贝第一层级的对象属性或数组元素,还是递归拷贝所有层级的对象属性和数组元素?
怎么检验深拷贝成功
改变任意一个新对象/数组中的属性/元素,都不改变原对象/数组
只对第一层级做拷贝
深拷贝数组,且只拷贝第一层
// 1.用for循环遍历
let array = [1,3,5,7]
function copy (array) {
let newArray = []
for(let item of array) {
newArray.push(item);
}
return newArray;
}
var copyArray = copy(array);
copyArray[0] = 10;
console.log(array); // [1,3,5,7]
console.log(copyArray); // [10,3,5,7]
//2.数组操作方法slice()
let array = [1,3,5,7]
var copyArray = array.slice(0);
copyArray[0] = 10
console.log(array); // [1,3,5,7]
console.log(copyArray); // [10,3,5,7]
//slice() 方法返回一个从已有的数组中截取一部分元素片段组成的新数组 (`不改变原来的数组!`)
//用法:array.slice(start,end) ,start表示是起始元素的下标, end表示的是终止元素的下标
//当slice()不带任何参数的时候,默认返回一个长度和原数组相同的新数组
//3.数组操作方法concat()
let array = [1,3,5,7]
var copyArray = array.concat();
copyArray[0] = 10
console.log(array); // [1,3,5,7]
console.log(copyArray); // [10,3,5,7]
concat()方法用于连接两个或多个数组( 该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本)
用法:array.concat(array1,array2,......,arrayN)
深拷贝事情可能不是这么简单,我上面的标题是 “深拷贝数组(只拷贝第一级数组元素)”,这里说的意思是对于一级数组元素是基本类型变量(如number,String,boolean)的简单数组, 上面这三种拷贝方式都能成功,但对第一级数组元素是对象或者数组等引用类型变量的数组,上面的三种方式都将失效
如下:
var array = [
{ number: 1 },
{ number: 2 },
{ number: 3 }
];
var copyArray = array.slice();
copyArray[0].number = 100;
console.log(array); // [{number: 100}, { number: 2 }, { number: 3 }]
console.log(copyArray); // [{number: 100}, { number: 2 }, { number: 3 }]
深拷贝对象
1.直接遍历
var obj = {
name: '彭湖湾',
job: '学生'
}
function copy (obj) {
let newObj = {};
for (let item in obj ){
newObj[item] = obj
}
return newObj;
}
var copyObj = copy(obj);
copyObj.name = '我才不是彭湖湾呢! 哼 (。・`ω´・)';
console.log(obj); // {name: "彭湖湾", job: "学生"}
console.log(copyObj); // {name: "我才不是彭湖湾呢! 哼 (。・`ω´・)", job: Object}
2.ES6的Object.assign
var obj = {
name: '彭湖湾',
job: '学生'
}
var copyObj = Object.assign({}, obj);
copyObj.name = '我才不叫彭湖湾呢! 哼 (。・`ω´・)';
console.log(obj); // {name: "彭湖湾", job: "学生"}
console.log(copyObj); // {name: "我才不叫彭湖湾呢! 哼 (。・`ω´・)", job: "学生"}
3.ES6的扩展运算符
var obj = {
name: '彭湖湾',
job: '学生'
}
var copyObj = { ...obj }
copyObj.name = '我才不叫彭湖湾呢! 哼 (。・`ω´・)'
console.log(obj.name) // 彭湖湾
console.log(copyObj.name) // 我才不叫彭湖湾呢! 哼 (。・`ω´・)
对多层嵌套对象,很遗憾,上面三种方法,都会失败:
var obj = {
name: {
firstName: '彭',
lastName: '湖湾'
},
job: '学生'
}
var copyObj = Object.assign({}, obj)
copyObj.name.lastName = '湖水的小浅湾';
console.log(obj.name.lastName); // 湖水的小浅湾
console.log(copyObj.name.lastName); // 湖水的小浅湾
拷贝所有层级
- 不仅拷贝第一层级,还能够拷贝数组或对象所有层级的各项值
- 不是单独针对数组或对象,而是能够通用于数组,对象和其他复杂的JSON形式的对象
1.JSON.parse(JSON.stringify(XXXX))
var array = [
{ number: 1 },
{ number: 2 },
{ number: 3 }
];
var copyArray = JSON.parse(JSON.stringify(array))
copyArray[0].number = 100;
console.log(array); // [{number: 1}, { number: 2 }, { number: 3 }]
console.log(copyArray); // [{number: 100}, { number: 2 }, { number: 3 }]
2.手动写递归
var array = [
{ number: 1 },
{ number: 2 },
{ number: 3 }
];
function copy (obj) {
var newobj = obj.constructor === Array ? [] : {};
if(typeof obj !== 'object'){
return obj;
}
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 }]
存在大量深拷贝需求的代码——immutable提供的解决方案
实际上,即使我们知道了如何在各种情况下进行深拷贝,我们也仍然面临一些问题: 深拷贝实际上是很消耗性能的 。(我们可能只是希望改变新数组里的其中一个元素的时候不影响原数组,但却被迫要把整个原数组都拷贝一遍,这不是一种浪费吗?) 所以,当你的项目里有大量深拷贝需求的时候,性能就可能形成了一个制约的瓶颈了。
immutable的作用:
通过immutable引入的一套API,实现:
1.在改变新的数组(对象)的时候,不改变原数组(对象)
2.在大量深拷贝操作中显著地减少性能消耗
const { Map } = require('immutable')
const map1 = Map({ a: 1, b: 2, c: 3 })
const map2 = map1.set('b', 50)
map1.get('b') // 2
map2.get('b') // 50