function deepClone(obj) {
if (typeof obj!== 'object' || obj === null) {
return obj;
}
const isArray = Array.isArray(obj);
const clone = isArray? [] : {};
for (const key in obj) {
if (Object.hasOwnProperty.call(obj, key)) {
const desc = Object.getOwnPropertyDescriptor(obj, key);
if (desc.configurable === false && desc.writable === false) {
Object.defineProperty(clone, key, {
...desc,
value: deepClone(desc.value)
});
} else if (desc.configurable === false && desc.writable === true) {
Object.defineProperty(clone, key, {
...desc,
value: deepClone(desc.value)
});
} else {
clone[key] = deepClone(obj[key]);
}
}
}
return clone;
}
// 测试
const original = {
a: 1,
b: {
c: 2,
d: {
e: 3
}
},
f: Object.defineProperty({}, 'g', {
value: 4,
writable: false,
configurable: false
})
};
try {
const cloned = deepClone(original);
console.log(cloned);
// 尝试修改不可配置且不可写的属性
cloned.f.g = 5;
} catch (error) {
console.error(error.message);
}
实现思路
- 基本类型处理:如果传入的
obj
不是对象或者是 null
,直接返回 obj
,因为基本类型不需要克隆。
- 判断数组或对象:使用
Array.isArray
判断 obj
是数组还是对象,然后创建相应类型的空克隆对象 clone
。
- 遍历属性:通过
for...in
循环遍历 obj
的所有属性,并使用 Object.hasOwnProperty.call
确保只处理自身属性。
- 获取属性描述符:使用
Object.getOwnPropertyDescriptor
获取每个属性的描述符 desc
。
- 处理不同属性特性:
- 如果
configurable
为 false
且 writable
为 false
,通过 Object.defineProperty
将该属性以相同特性克隆到 clone
中,同时递归克隆属性值。
- 如果
configurable
为 false
且 writable
为 true
,同样通过 Object.defineProperty
克隆属性,并递归克隆属性值。
- 其他情况(
configurable
为 true
),直接将属性值递归克隆到 clone
中。
- 返回克隆对象:最后返回克隆好的对象
clone
。在测试部分,尝试修改不可配置且不可写的属性时会触发错误,因为克隆对象保持了原对象的属性特性。