Mark24
记录灵感、技术、思考
JavaScript深拷贝浅拷贝
一、浅拷贝以及方法
引用类型,a,b保存内存中同一个地址空间,当a修改了变量,b也发生变化,因为修改的是内存对象,他们共同引用了同一个内存对象,发生改变。这种就是浅拷贝。
浅拷贝方法
-
引用变量赋值:直接把一个引用变量,赋值给另一个变量。完成浅拷贝
-
遍历并赋值给新对象:
const shallowCopy = (obj) => {
// 判断参数是数组还是对象
const result = Array.isArray(obj) ? [] : {};
for(let key in obj) {
// 使用hasOwnProperty来判断是否是自身属性
// 只拷贝自身属性,不拷贝原型链上的属性
if(obj.hasOwnProperty(key)){
result[key] = obj[key];
}
}
return result;
}
二、深拷贝以及方法
深度拷贝后,两个对象在内存中完全不同,互不影响。
首先要明白,JS里面,深度复制没有一个完美答案。 当一个数据可JSON,他的深度复制才有意义。
深拷贝方法
- JSON转化
JSON的方法对,属性值是undefined,正则,函数,以及产生循环引用的数据对象都不行。
// JSON的方法对,undefined,循环引用,正则,函数,都不行
const obj = {
arr: [111, 222],
obj: { key: '对象' },
a: () => { console.log('函数') },
date: new Date(),
reg: /正则/ig,
testValue: undefined,
testValue: null,
}
const shadowCopyObj = JSON.parse(JSON.stringify(obj))
console.log(shadowCopyObj)
// output
// { arr: [ 111, 222 ],
// obj: { key: '对象' },
// date: '2020-04-20T08:03:01.178Z',
// reg: {},
// testValue: null }
- for…in 递归
无法解决 函数,正则,日期,循环引用
const obj = {
arr: [111, 222],
obj: { key: '对象' },
a: () => { console.log('函数') },
date: new Date(),
reg: /正则/ig,
testValue: undefined,
testValue: null,
}
function isObj(obj) {
return (typeof obj === 'object' || typeof obj === 'function') && obj !== null
}
function deepCopy(obj) {
let tempObj = Array.isArray(obj) ? [] : {}
for (let key in obj) {
tempObj[key] = isObj(obj[key]) ? deepCopy(obj[key]) : obj[key]
}
return tempObj
}
const deepCopyObj = deepCopy(obj)
console.log(deepCopy(obj))
// output"
// { arr: [ 111, 222 ],
// obj: { key: '对象' },
// a: {},
// date: {},
// reg: {},
// testValue: null }
- 结构性复制,可惜是异步的
//https://developer.mozilla.org/zh-CN/docs/Web/Guide/API/DOM/The_structured_clone_algorithm#%E7%9B%B8%E5%85%B3%E9%93%BE%E6%8E%A5
没有完整的深拷贝方案。这是一个从上层不被支持的功能。
三、实用性的深拷贝实现
更新方法:
2023年03月17日,一个更可塑的方法。 尤其是对于深拷贝一个函数,关注他的处理方式。
function deepCopy(obj, hash = new WeakMap()) {
if (obj === null) return obj;
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj);
if (typeof obj === 'function') {
return new Function(`return ${obj.toString()};`)();
}
if (typeof obj !== 'object') return obj;
if (hash.has(obj)) return hash.get(obj);
// 使用obj构建原型
let result = new Object.create(obj); // 或者 = new obj.constructor();
hash.set(obj, result);
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
// 再次传入 hash 很重要,不传递会爆栈
result[key] = deepCopy(obj[key], hash);
}
}
return result;
}
// 示例
let originalObj = {a: 1, b: {c: 2}, f: function(){ console.log('origin'); return 'origin'}};
originalObj.self = originalObj;
let copiedObj = deepCopy(originalObj);
console.log(copiedObj); // 输出:{a: 1, b: {c: 2}, self: [Circular]}
originalObj.b.c = 3;
console.log(copiedObj); // 输出:{a: 1, b: {c: 2}, self: [Circular]}
console.log(originalObj.f === copiedObj.f) // false 完成函数深拷贝