JS基础总结 - 深浅拷贝
- 浅拷贝:以对象为例,如果对象的属性是值类型,拷贝的是这个值类型的值;如果是引用类型,拷贝的就是内存地址。
- 深拷贝:将一个对象从内存中完整的拷贝一份出来。还是以对象为例,如果对象的属性是值类型,拷贝这个值到栈中;如果是引用类型,那么就在堆内存中开辟一个新的区域存放这个引用类型的原始对象(而不是内存地址)。这样对新对象的修改,不会影响到原对象。
浅拷贝:
-
测试对象
const obj1 = {
name: 'xxx',
age: 20,
address: {
city: '北京'
},
arr: ['a', 'b', 'c']
}
-
实现方式
// 方法一: 遍历
function simpleClone (oldObj) {
let newObj = {}
for(const i in oldObj) {
newObj[i] = oldObj[i]
}
return newObj
}
// 方法二: Object.creat()
let obj2 = Object.create(obj1)
// 方法三:Object.assign()
let obj2 = Object.assign({}, obj1)
// 方法四: 直接拷贝
// * 这种方式,即使属性是基本类型,obj1 和 obj2 也会相互影响
let obj2 = obj1
// 测试
obj2.address.city = '上海'
consloe.log(obj1.address.city) // 上海
深拷贝
-
基础版
缺点: 不支持 undefined、function类型
const obj1 = { a: 'aaa', b: null, c: [], d: {}, c: undefined, d: function() {} } const obj2 = JSON.parse(JSON.stringify(obj1)) console.log(obj2) // -> { a:'aaa', b:null, c:[], d:{}} // undefined && function 没有被拷贝
-
升级版
缺点: 不支持 循环引用
function clone(target) { if (typeof target !== 'object' || target == null) { // ↑ == 用于兼并判断 undefined,这里没必要因为第一个条件已判断,但这也是一种编程小技巧,所以还是用 == return target } let result = Array.isArray(target) ? [] : {} for (const key in target) { if (target.hasOwnProperty(key)) { // 保证 key 不是原型属性 result[key] = clone(target[key]) } } return result }
-
加强版
缺点: 没考虑性能优化
function clone(target, map = new Map()) { if (typeof target !== 'object' || target == null) { return target } if (map.get(target)) { return map.get(target) } let result = Array.isArray(target) ? [] : {} map.set(target, result) for (const key in target) { result[key] = clone(target[key], map) } return result } // 测试 const target = { field1: 1, field2: undefined, field3: { child: 'child' }, field4: [2, 4, 8] }; target.target = target const test = clone(target) console.log(test)
-
终极版
使用 WeakMap 进行垃圾回收, 完美!!! ღゝ◡╹)ノ♡
WeakMap
对象是一组键/值对的集合,其中的键是弱引用的。其键必须是对象(Object
类型),而值可以是任意的。WeakMap 持有的是每个键对象的“弱引用”,这意味着在没有其他引用存在时垃圾回收能正确进行。原生 WeakMap 的结构是特殊且有效的,其用于映射的 key 只有在其没有被回收时才是有效的。
由于这样的弱引用,
WeakMap
的 key 是不可枚举的 (没有方法能给出所有的 key)。如果key 是可枚举的话,其列表将会受垃圾回收机制的影响,从而得到不确定的结果。因此,如果你想要这种类型对象的 key 值的列表,你应该使用Map
。以下代码帮你理解,WeakMap:
// Map let key = { name : 'ConardLi'} const target = new Map() target.set(key, 'hello') key = null // * 虽然我们手动将 key ,进行释放,但 target 依然对 key 存在强引用关系,所以这部分内存无法被释放。 // WeakMap let key = { name : 'ConardLi'} const target = new WeakMap() target.set(key, 'code秘密花园') key = null // * target 和 obj 存在的就是弱引用关系,当下一次垃圾回收机制执行时,这块内存就会被释放掉。
终极版代码
function clone(target, map = new WeakMap()) { if (typeof target !== 'object' || target == null) { return target } if (map.get(target)) { return map.get(target) } let result = Array.isArray(target) ? [] : {} map.set(target, result) for (const key in target) { result[key] = clone(target[key], map) } return result } // 测试 const target = { field1: 1, field2: undefined, field3: { child: 'child' }, field4: [2, 4, 8] }; target.target = target const test = clone(target) console.log(test)