JavaScript深浅拷贝的实现
前置知识
- 对象类型在赋值的过程中其实是复制了地址,从而会导致改变了一方其他也都被改变的情况
let a = {
age: 1
}
let b = a
a.age = 2
console.log(b.age) // 2
浅拷贝
- Object.assign : 拷贝所有的属性值到新的对象中,如果属性值是对象的话,拷贝的是地址,所以并不是深拷贝
let a = {
age: 1
}
let b = Object.assign({}, a)
a.age = 2
console.log(b.age) // 1
- 通过展开运算符 ... 来实现浅拷贝
let a = {
age: 1
}
let b = {...a}
a.age = 2
console.log(b.age) // 1
深拷贝
- JSON.parse(JSON.stringify(object))
- 会忽略 undefined
- 会忽略 symbol
- 不能序列化函数
- 不能解决循环引用的对象,会报错抛出异常
let a = {
age: 1,
jobs: {
first: 'FE'
}
}
let b = JSON.parse(JSON.stringify(a))
a.jobs.first = 'native'
console.log(b.jobs.first) // FE
let a = {
age: undefined,
sex: Symbol('male'),
jobs: function() {},
name: 'yck'
}
let b = JSON.parse(JSON.stringify(a))
console.log(b) // {name: "yck"}
- 递归
function isObject(obj) {
//Object.prototype.toString.call(obj) === '[object Object]'要保留数组形式,用在这里并不合适
return typeof obj === 'object' && obj != null
}
function cloneDeep1(obj){
if(!isObject(obj)) return obj
var newObj = Array.isArray(obj)? [] : {}
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = isObject(obj[key])? cloneDeep1(obj[key]) : obj[key]
}
}
return newObj
}
- 问题:递归方法最大的问题在于爆栈,当数据的层次很深是就会栈溢出,例如循环引用
var a = {
name: "muyiy",
a1: undefined,
a2: null,
a3: 123,
book: {title: "You Don't Know JS", price: "45"}
}
a.circleRef = a
// TypeError: Converting circular structure to JSON
JSON.parse(JSON.stringify(a))
//Uncaught RangeError: Maximum call stack size exceeded at Object.hasOwnProperty (<anonymous>)
cloneDeep1(a)
- 解决方法:循环检测(设置一个数组或者哈希表存储已拷贝过的对象,当检测到当前对象已存在于哈希表中时,取出该值并返回即可)
//哈希表
function cloneDeep3(source, hash = new WeakMap()) {
if (!isObject(source)) return source;
if (hash.has(source)) return hash.get(source); // 新增代码,查哈希表
var target = Array.isArray(source) ? [] : {};
hash.set(source, target); // 新增代码,哈希表设值
for(var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
if (isObject(source[key])) {
target[key] = cloneDeep3(source[key], hash); // 新增代码,传入哈希表
} else {
target[key] = source[key];
}
}
}
return target;
}
//数组
function cloneDeep3(source, uniqueList) {
if (!isObject(source)) return source;
if (!uniqueList) uniqueList = []; // 新增代码,初始化数组
var target = Array.isArray(source) ? [] : {};
// 数据已经存在,返回保存的数据
var uniqueData = find(uniqueList, source);
if (uniqueData) {
return uniqueData.target;
};
// 数据不存在,保存源数据,以及对应的引用
uniqueList.push({
source: source,
target: target
});
for(var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
if (isObject(source[key])) {
target[key] = cloneDeep3(source[key], uniqueList); // 新增代码,传入数组
} else {
target[key] = source[key];
}
}
}
return target;
}
// 新增方法,用于查找
function find(arr, item) {
for(var i = 0; i < arr.length; i++) {
if (arr[i].source === item) {
return arr[i];
}
}
return null;
}
链接:https://juejin.cn/post/7010707434473783309