# 深浅拷贝实现
# 一、基本概念
深拷贝和浅拷贝是只针对Object和Array这样的引用数据类型
对象与原始类型其中一个基本的区别是:对象“通过引用的形式”被存储和拷贝
原始类型值:字符串,数字,布尔值 —— 被“作为整体”赋值/拷贝。
let message = "Hello!";
let phrase = message;
结果我们就有了两个独立的变量,每个都存储着字符串 "Hello!"
。
而变量存储的不是对象自身,而是该对象的“内存地址”,换句话说就是一个对该对象的“引用”
当一个对象变量被拷贝 —— 引用则被拷贝,而该对象并没有被复制。
let user = { name: "John" };
let admin = user; // 拷贝引用
此时两个变量引用的是同一个对象
**
所以我们需要:
- **浅拷贝 **对对象而言,它的第一层属性值如果是基本数据类型则完全拷贝一份数据,如果是引用类型就拷贝内存地址。
- **深拷贝 **会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。
接下来谈谈各自的实现
# 二、浅拷贝
# 2.1 Object.assign
let obj={
str:'first',
objDeep:{
name:'jack',
age:22
},
fun(){}
}
let objCopy=Object.assign({},obj)
objCopy.str='change'
objCopy.objDeep.name='tom'
objCopy.fun=function (param) {
return param
}
console.log(obj);
console.log(objCopy);
可以看出,确实只有第一层是真正的“拷贝”
# 2.2 展开运算符
let obj={
str:'first',
objDeep:{
name:'jack',
age:22
},
fun(){}
}
let objCopy={...obj}//ES6展开运算符
objCopy.str='change'
objCopy.objDeep.name='tom'
objCopy.fun=function (param) {
return param
}
console.log(obj);
console.log(objCopy);
结果和2.1一致
# 2.3 Array.prototype.slice
let arr=[
'first',
{
name:'jack',
age:22
},
function(){}
]
let arrCopy=arr.slice(0) // slice 第一个参数是从哪一位开始截取,第二参数为空就会一直截取到最后
arrCopy[0]='change'
arrCopy[1].name='tom'
arrCopy[2]=function (param) {
return param
}
console.log(arr);
console.log(arrCopy);
# 2.4 Array.prototype.concat
let arr=[
'first',
{
name:'jack',
age:22
},
function(){}
]
let arrCopy=[].concat(arr)
arrCopy[0]='change'
arrCopy[1].name='tom'
arrCopy[2]=function (param) {
return param
}
console.log(arr);
console.log(arrCopy);
结果打印出来和上面是一样的
✨总结可以发现,实际上对于数组来说, 只要不修改原数组, 重新返回一个新数组就可以实现浅拷贝,比如说map、filter、reduce等方法
# 三、深拷贝
# 3.1 JSON.parse(JSON.stringify(object))
JSON.parse
不能处理有函数的对象,因为JSON中没有这个概念,但是一般常见的对象用这个也足够了
let obj={
str:'first',
objDeep:{
name:'jack',
age:22
},
fun(){}
}
let objCopy=JSON.parse(JSON.stringify(obj))
console.log(objCopy);
拷贝后函数丢失
# 3.2 lodash
JavaScript 库 lodash 中的 _.cloneDeep(obj)
这里就说一下基本用法
var _ = require('lodash');
var obj1 = {
a: 1,
b: { f: { g: 1 } },
c: [1, 2, 3]
};
var obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f);
// false
# 3.3 手写深拷贝⭐
基本思路:
- 判断类型
- 接收用数据类型判断后初始化
- 一层一层嵌套判断内部数据,拿到基本数据类型(Number、String、Boolean、Null、undefined、Symbol、Bigint)或者函数后停止递归
//定义检测数据类型的功能函数
function checkedType(target) {
return Object.prototype.toString.call(target).slice(8, -1)
}
//实现深度克隆---对象/数组
function clone(target) {
//判断拷贝的数据类型
//初始化变量result 成为最终克隆的数据
let result, targetType = checkedType(target)
if (targetType === 'Object') {
result = {}
} else if (targetType === 'Array') {
result = []
} else {
return target
}
//遍历目标数据
for (let i in target) {
//获取遍历数据结构的每一项值。
let value = target[i]
//判断目标结构里的每一值是否存在对象/数组
if (checkedType(value) === 'Object' ||
checkedType(value) === 'Array') { //对象/数组里嵌套了对象/数组
//继续遍历获取到value值
result[i] = clone(value)
} else { //获取到value值是基本的数据类型或者是函数。
result[i] = value;
}
}
return result
}
测试:
let obj={
str:'first',
objDeep:{
name:'jack',
age:22
},
fun(){}
}
let objCopy=clone(obj)
objCopy.str='change'
objCopy.objDeep.name='tom'
objCopy.fun=function (param) {
return param
}
console.log(obj);
console.log(objCopy);
console.log("===========Test Array==============");
let arr=[
'first',
{
name:'jack',
age:22
},
function(){}
]
let arrCopy=clone(arr)
arrCopy[0]='change'
arrCopy[1].name='tom'
arrCopy[2]=function (param) {
return param
}
console.log(arr);
console.log(arrCopy);
打印结果:
到此为止就基本实现了。