前言
在前端开发的工作中经常需要对数据进行处理,这时候会经常使用到浅拷贝和深拷贝先拷贝原有数据再进行处理,以下就是常用的方法总结,我将以代码的形式来带大家去使用和理解这些方式
一、浅拷贝常用方法
1. Object.assign
该方法是es6新出的一个方法,可以直接拷贝对象进行数据的处理,在vue里使用比较普遍
使用示例:
let obj = {
name: '李信',
age: 40,
children: {
name: '李明',
age: 18
}
}
let newObj = Object.assign({},obj)
obj.name = '兰陵王'
console.log(obj); // obj.name = '兰陵王'
console.log(newObj); // newObj.name = '李信'
细节: 此时浅拷贝的是对象的一级属性,所以当原始obj.name改变了以后newObj.name不会受到影响,但是对于二级属性如果还有对象就还是相同的引用地址,如下所示
let obj = {
name: '李信',
age: 40,
children: {
name: '李明',
age: 18
}
}
let newObj = Object.assign({},obj)
obj.children.name = '马超',
console.log(obj); //obj.children.name = '马超'
console.log(newObj); // newObj.children.name = '马超'
细节: 改变obj的chidren对象里面的值以后拷贝的newObj的值也发生了改变,说明这个方法实现的只是一级属性的浅拷贝,对于多级属性无法进行更深的拷贝
这个方法还有个常用的用法是可以实现对象的拷贝合并
let obj = {
name: '李信',
age: 40,
children: {
name: '李明',
age: 18
}
}
let obj2 = {
name: '海王',
age: 22
}
let obj3 = {
gender: 1
}
let testObj = Object.assign({}, obj,obj3)
let newObj = Object.assign({},obj, obj2)
console.log(testObj);
console.log(newObj);
结果如下图所示,合并对象大概分两种情况,正常合并是直接将两个对象属性名都合并拷贝到新对象,但是如果后面的对象有前面对象相同属性名,那么就会出现覆盖情况,这是需要值得去强调的
2. 数组的slice和concat方法
这两个方法是数组的api,一个是截取数组的值,另一个可以用来连接数组,实际上还可以用来进行数组的浅拷贝复制
使用示例:
let arr = ['刘备','关羽','张飞','赵云','马超']
let newArr = arr.slice()
let newarr = arr.concat()
arr[0] = '曹操'
console.log(arr); // arr[0] = '曹操'
console.log(newArr); // newArr[0] = '刘备'
console.log(newarr); // newarr[0] = '刘备'
细节: 从上面可以看出这两个api也是实现了数组的拷贝,改变原来的数组arr[0],拷贝后的新数组却没有受到影响,完美的实现了复制,但是这个方法也只是实现一级属性的拷贝,也就是浅拷贝
我们工作中拿到的数据格式一般是数组对象,对于数组对象这种格式这两个方法就只能实现一级属性的拷贝,如下所示
let arr = [
{
id: 111,
name: '赵云',
age: 18
},
{
id: 222,
name: '曹操',
age:26
},
{
id: 333,
name: '刘备',
age: 25
}
]
let newArr = arr.slice()
let newarr = arr.concat()
arr[0].name = '孙权'
console.log(arr); // arr[0].name = '孙权'
console.log(newArr);// newArr[0].name = '孙权'
console.log(newarr);// newArr[0].name = '孙权'
细节: 从上面就可以看出如果数组里面是对象这种数据,用这两个方法实现的就只是无法进行多级的拷贝,所以这两个api只能实现简单数组的浅拷贝
3. Array.from
这个方法也是es6的一个新方法,也是常用作浅拷贝数组的一个常用方法
使用示例:
let arr = [
{
id: 111,
name: '赵云',
age: 18
},
{
id: 222,
name: '曹操',
age:26
},
{
id: 333,
name: '刘备',
age: 25
}
]
let newArr = Array.from(arr)
arr[0] = {
id: 666,
name: '海风',
age: 66
}
console.log(arr);
console.log(newArr);
细节: 从以上结果可以看出,该方法也是实现了数组的一级属性拷贝,改变原始数组的一级属性不会影响到拷贝后的新数组,但是如果改变数组对象里面的值同上面一样也是无法多级拷贝,
在这里我就不去重复的演示了
4. es6展开运算符...
这个应该算是浅拷贝最简单的方式,而且还很强大,数组和对象都可以用它来进行浅拷贝,话不多说,直接上代码
let obj = {
name: '张三',
age: 26
}
let newObj = {...obj}
obj.name = '李四'
console.log(obj) // obj.name="李四"
console.log(newObj)// newObj.name ="张三"
细节: 上面就是使用展开运算符对对象进行的浅拷贝,实现的一级属性拷贝,原始数组改变以后并不会影响到新数组
let arr = [
{
id: 111,
name: '赵云',
age: 18
},
{
id: 222,
name: '曹操',
age:26
},
{
id: 333,
name: '刘备',
age: 25
}
]
let newarr = [...arr]
arr[0] = {
id: 666,
name: '海风',
age: 66
}
console.log(arr);
console.log(newarr);
细节: 可以看出来数组浅拷贝实现的结果同上面几种也是相同的,但是展开运算符实现的也仅仅只是浅拷贝,并没有实现深层次属性的拷贝,如下图就可以看出来
let arr = [
{
id: 111,
name: '赵云',
age: 18
},
{
id: 222,
name: '曹操',
age:26
},
{
id: 333,
name: '刘备',
age: 25
}
]
let newarr = [...arr]
arr[0].name = "孙权"
console.log(arr); // arr[0].name = "孙权"
console.log(newarr);// newarr[0].name = "孙权"
二、深拷贝常用方法
从上面可以看出来浅拷贝对于数组对象这种格式的数据无法进行深层次多级的拷贝,所以有些情景就需要用到深拷贝来实现数据的复制
1.Json.parse && Json.stringify
这个方法是深拷贝里面最容易的一种方法,利用json的api来对数组对象进行深拷贝,具体代码如下
let arr = [
{
id: 111,
name: '赵云',
age: 18
},
{
id: 222,
name: '曹操',
age:26
},
{
id: 333,
name: '刘备',
age: 25
}
]
let newArr = JSON.parse(JSON.stringify(arr))
arr[0].name = "孙权"
console.log(arr); // arr[0].name = "孙权"
console.log(newArr); // arr[0].name = "赵云"
细节: 先将数组对象转成json格式,然后再转变回数组,这样就可以实现深拷贝新数组,新的数组和原始数组彻底没有关系,不过这个方法也有一些缺陷,在这里我简单说一下这个方法大概的缺陷
- 当被转化的对象里面含有时间对象这种格式的数据会被转化为字符串,代码如下
let arr = [
{
id: 111,
name: '赵云',
age: 18,
date: new Date(1280977330000)
},
{
id: 222,
name: '曹操',
age:26,
date: new Date(1280977330000)
},
{
id: 333,
name: '刘备',
age: 25,
date: new Date(1280977330000)
}
]
let newArr = JSON.parse(JSON.stringify(arr))
console.log(newArr)
- 当对象里面有regexp格式的数据通过JSON拷贝的对象里面该数据变成了空对象,代码如下所示
let arr = [
{
id: 111,
name: '赵云',
age: 18,
reg: /^139$/
},
{
id: 222,
name: '曹操',
age:26,
reg: /^139$/
},
{
id: 333,
name: '刘备',
age: 25,
reg: /^139$/
}
]
let newArr = JSON.parse(JSON.stringify(arr))
console.log(newArr)
- 如果拷贝的对象里面有function,则拷贝以后该数据会丢失
let arr = [
{
id: 111,
name: '赵云',
age: 18,
log: () => {
console.log(123);
}
},
{
id: 222,
name: '曹操',
age:26,
log: () => {
console.log(123);
}
},
{
id: 333,
name: '刘备',
age: 25,
log: () => {
console.log(123);
}
}
]
let newArr = JSON.parse(JSON.stringify(arr))
console.log(newArr)
2. 递归深拷贝
function deepCopy(obj) {
if(typeof obj !== 'object') return
let newObj = obj instanceof Array ? [] : {}
for(let key in obj) {
// 含有该obj的可枚举属性才进行拷贝
if(obj.hasOwnProperty(key)) {
if(typeof obj[key] === "object") {
newObj[key] = deepCopy(obj[key])
}
else {
newObj[key] = obj[key]
}
}
}
return newObj
}
let obj = {
name: '曹操',
age: 40,
children: {
name: '曹丕',
age:30,
},
}
let newobj = deepCopy(obj)
obj.children.name = "曹忠"
console.log(obj);
console.log(newobj);
这个方法属于递归版的深拷贝实现方法,这个是简易版的实现方法,一些特殊情景和JSON一样也无法进行拷贝,我将专门用一节来讲一下终极版的深拷贝方法