今天在项目中用到了深拷贝,以此来记录一下。
介绍
JS中数据分为原始类型和引用类型。
对于原始类型来说,并没有深浅拷贝的区别。所以深浅拷贝只针对于引用类型来说。
浅拷贝: 只进行一层复制。对于引用类型的数据,只拷贝了地址,所以复制的引用类型的数据还是共享地址的。
深拷贝: 深拷贝之后的对象不会和原拷贝对象相互影响
深拷贝的库: lodash
浅拷贝方法: clone
深拷贝方法: cloneDeep
例子:
1) 两个对象指向同一地址, == 运算符会返回true
const obj = {};
const newObj = obj;
console.log(obj == newObj); // true
2) 两个对象指向不同的值, == 运算符会返回false
const obj = {};
const newObj = {};
console.log(obj == newObj); // false
这时候使用lodash中的clone, 就可以实现一层的浅拷贝。obj.person和newObj.person指向同一个对象
import { clone } from "lodash";
const obj = {
person: {
name: "yingxu",
},
};
const newObj = clone(obj);
obj.person.name = "xxx"; // 改变原来的对象
console.log("原来的对象", obj);
console.log("新的对象", newObj);
console.log("更深层的指向同一地址", obj.person == newObj.person); // true
这时候,如果要让他们指向不同的对象,可以使用cloneDeep
import { cloneDeep } from "lodash";
const obj = {
person: {
name: "yingxu",
},
};
const newObj = cloneDeep(obj);
obj.person.name = "xxx"; // 改变原来的对象
console.log("原来的对象", obj);
console.log("新的对象", newObj);
console.log("更深层的对象指向同一地址", obj.person == newObj.person); // false
实现浅拷贝的方法:
1) Object.assign
const obj = {
name: "yingxu",
};
const newObj = Object.assign({}, obj);
obj.name = "xxx"; // 改变原来的对象
console.log(newObj); // { name: 'yingxu' } 新对象不变
console.log(obj == newObj); // false 两者指向不同地址
2) 数组的 contat 和 slice方法
const arr = ['yingxu', 'is', 'handsome']
const newArr = arr.slice(0)
arr[2] = 'rich' // 改变原来的数组
console.log(newArr) // ['yingxu', 'is', 'handsome']
console.log(arr == newArr) // false 两者指向不同地址
// -----------------------------------------------
const arr = ["yingxu", "is", "handsome"];
const newArr = [].concat(arr);
arr[2] = "rich"; // 改变原来的数组
console.log(newArr); // ['yingxu', 'is', 'handsome'] // 新数组不变
console.log(arr == newArr); // false 两者指向不同地址
3) Array.from
const arr = ["yingxu", "is", "handsome"];
const newArr = Array.from(arr);
arr[2] = "rich"; // 改变原来的数组
console.log(newArr); // ['yingxu', 'is', 'handsome']
console.log(arr == newArr); // false 两者指向不同地址
4) 扩展运算符
const arr = ["yingxu", "is", "handsome"];
const newArr = [...arr];
arr[2] = "rich"; // 改变原来的数组
console.log(newArr); // ['yingxu', 'is', 'handsome'] // 新数组不变
console.log(arr == newArr); // false 两者指向不同地址
实现深拷贝的方法
1) JSON.parse(JSON.stringify(obj));
const obj = {
person: {
name: "yingxu",
},
};
const newObj = JSON.parse(JSON.stringify(obj));
obj.person.name = "xxx"; // 改变原来的深层对象
console.log(newObj); // { person: { name: 'yingxu' } } 新的深层对象不变
这种实现方法有三种弊端
1> 会忽略undefined, Symbol和函数
const obj = {
a: undefined,
b: Symbol("b"),
c: function () {},
};
const newObj = JSON.parse(JSON.stringify(obj));
console.log(newObj); // {}
2> NaN, Ininity, -Infinity会被序列化为null
const obj = {
a: NaN,
b: Infinity,
c: -Infinity,
};
const newObj = JSON.parse(JSON.stringify(obj)); // {a: null, b: null, c:null}
console.log(newObj);
3> 而且无法解决循环引用的问题
const obj = {
a: 1,
};
obj.obj = obj;
const newObj = JSON.parse(JSON.stringify(obj)); // 报错
所以只适合日常开发中一些简单的对象。
2) 简单版本的深拷贝: 递归调用
function deepClone(target) {
if (typeof target !== "object") {
// 如果是原始类型,无需继续拷贝,直接返回
return target;
}
// 如果是引用类型,递归实现每一层的拷贝
const cloneTarget = {}; // 定义一个克隆对象
for (const key in target) {
// 遍历原对象
cloneTarget[key] = deepClone(target[key]); // 递归拷贝每一层
}
return cloneTarget; // 返回克隆对象
}
但是这里没有解决各种类型比如数组,日期,正则和null这些常用的类型,会将这些类型都序列化为{}。使用如下代码可以解决这些。
function deepClone(target) {
if (target === null) return target; // 处理 null
if (target instanceof Date) return new Date(target); // 处理日期
if (target instanceof RegExp) return new RegExp(target); // 处理正则
if (typeof target !== "object") return target; // 处理原始类型
// 处理对象和数组
const cloneTarget = new target.constructor(); // 创建一个新的克隆对象或克隆数组
for (const key in target) {
// 递归拷贝每一层
cloneTarget[key] = deepClone(target[key]);
}
return cloneTarget;
}
还有Symbol和循环引用等的处理,待之后学到这里再继续看
https://juejin.cn/post/7072528644739956773
Comments
Post a Comment