js深拷贝
实现深拷贝
定义方法deepClone,参数为对象类型(除几个基本类型外都是对象类型)
const deepClone = function (obj, hash = new WeakMap()) {
if (obj.constructor === Date) {
return new Date(obj) // 日期对象直接返回一个新的日期对象
}
if (obj.constructor === RegExp){
return new RegExp(obj) //正则对象直接返回一个新的正则对象
}
// 如果循环引用了就用 weakMap 来解决
if (hash.has(obj)) {
return hash.get(obj)
}
// 浅拷贝对象
let allDesc = Object.getOwnPropertyDescriptors(obj)
// 遍历传入参数所有键的特性
let cloneObj = Object.create(Object.getPrototypeOf(obj), allDesc)
// map存当前对象,obj必须为对象类型,因为WeakMap键必须是对象类型
hash.set(obj, cloneObj)
for (let key of Reflect.ownKeys(obj)) {
cloneObj[key] = (isComplexDataType(obj[key]) && typeof obj[key] !== 'function') ? deepClone(obj[key], hash) : obj[key]
}
return cloneObj
}
// 判断是不是Object类型
const isComplexDataType = obj => typeof obj === 'object' && (obj !== null)
- 定义方法传入对象,遍历属性拷贝到另一个对象,使用
Reflect.ownKeys
方法能够遍历对象的不可枚举属性
以及Symbol
类型; - 当参数为
Date、RegExp
类型,则直接生成一个新的实例返回; Object.create
方法创建一个新对象,并继承传入原对象的原型链;利用Object
的getOwnPropertyDescriptors
方法可以获得对象的所有属性描述,再传给Object.create
第二个参数,拷贝原对象。- 利用
WeakMap
类型存储拷贝对象,如果存在循环,则引用直接返回WeakMap
存储的值;因为WeakMap
是弱引用类型,可以有效防止内存泄漏。
测试
let obj = {
num: NaN,
str: '',
boolean: false,
undef: undefined,
nul: null,
obj: { name: 'huiyani', age: 20, desc: { hobby: "打电动" } },
arr: [1, 2, 3],
func: function () { console.log('huiyani') },
date: new Date(),
reg: new RegExp('/huiyani/ig'),
[Symbol('huiyani')]: "huiyani",
};
Object.defineProperty(obj, 'innumerable', {
enumerable: false, value: '不可枚举属性' }
);
obj = Object.create(obj, Object.getOwnPropertyDescriptors(obj))
obj.loop = obj // 设置loop成循环引用的属性
let cloneObj = deepClone(obj)
cloneObj.arr.push(4)
cloneObj.obj.desc.hobby = "又在打电动"
console.log('obj', obj)
console.log('cloneObj', cloneObj)
我们看一下结果,cloneObj
在 obj
的基础上进行了一次深拷贝,cloneObj
里的 arr
数组和obj
对象进行了修改,并未影响到 obj.arr
和cloneObj.obj
对象的变化,如下图所示
Object.create
1、参数传对象,返回一个对象,原型指向传入对象
let obj = Object.create({a:1})
console.log(obj)
2、传null,返回一个没有原型的对象
let obj1 = Object.create(null)
console.log(obj1)
3、传入属性描述符对象,默认属性描述配置为false
let obj2 = Object.create({}, { p: { value: 42 } })
console.log(obj2)
获取返回对象的属性描述符
console.log(Object.getOwnPropertyDescriptors(obj2))
writable
:属性的值是否能够被重新赋值
configurable
:是否可以删除该属性
enumerable
:此属性是否可以被枚举(使用for…in或Object.keys())
4、配置属性描述符
let des = { p: { value: 42, writable: true, enumerable: true, configurable: true } }
let obj3 = Object.create({}, des)
console.log(Object.getOwnPropertyDescriptors(obj3))
5、create方法第二个参数为一个对象的属性描述符
let obj4 = { a:1, b:2 }
let obj5 = { c:3, d:4 }
let obj6 = Object.create(obj4, Object.getOwnPropertyDescriptors(obj5))
console.log(obj6)
console.log(Object.getOwnPropertyDescriptors(obj6))
6、第一个参数传入类型决定返回类型
let desc = Object.getOwnPropertyDescriptors([1,2,3])
// 传入对象返回对象
let clone = Object.create({}, desc) // clone: {0: 1, 1: 2, 2: 3, length: 3}
// 传入数组返回数组
let clone = Object.create([], desc) // clone: [1, 2, 3]
1 游客 2025-09-01 11:27 回复
1
1 游客 2025-09-01 12:13 回复
1
@@rHrYe 游客 2025-09-01 12:15 回复
1
1 游客 2025-09-01 12:17 回复
1
1 游客 2025-09-01 12:18 回复
1