手写深拷贝时symbol.toprimitive不会被自动拷贝,因其为不可枚举symbol属性;需用object.getownpropertysymbols和object.defineproperty显式复制。

手写深拷贝时,若对象有自定义 Symbol.toPrimitive 方法,它本身**不会被递归拷贝**,也不会影响深拷贝逻辑——因为该 symbol 是一个**不可枚举、不可遍历的属性**,且只在类型转换(如 +、==、String())时被调用,不参与对象结构复制。
Symbol.toPrimitive 不会出现在 for...in / Object.keys / JSON.stringify 中
这是关键前提。自定义 Symbol.toPrimitive 是定义在对象自身(或原型上)的一个 symbol-keyed 方法,但:
-
Object.keys(obj)、for...in、Object.getOwnPropertyNames(obj)都**忽略 symbol 属性**; -
JSON.stringify()也**完全跳过所有 symbol 键**(包括toPrimitive); - 深拷贝实现若只遍历可枚举属性(常见做法),天然就不会触达它。
深拷贝需显式处理 symbol 属性(含 Symbol.toPrimitive)
如果希望拷贝后新对象也拥有相同的 Symbol.toPrimitive 行为,必须主动提取并设置 symbol 属性。标准深拷贝(如 structuredClone 或 lodash.cloneDeep)支持 symbol,但手写时需额外处理:
- 用
Object.getOwnPropertySymbols(obj)获取所有 own symbol 键; - 对每个 symbol 键,用
Object.getOwnPropertyDescriptor(obj, sym)拿描述符(含 value / get / set); - 用
Object.defineProperty(target, sym, descriptor)复制过去; - 注意:若原方法是箭头函数或绑定函数,直接赋值即可;若依赖闭包或 this,拷贝后行为不变(因函数本身未被“深”拆解)。
示例:支持 Symbol.toPrimitive 的手写深拷贝片段
以下是一个简化但可用的核心逻辑(仅处理 plain object、array、symbol 属性):
function deepClone(obj) {
if (obj === null || typeof obj !== 'object') return obj;
const target = Array.isArray(obj) ? [] : {};
// 拷贝自有可枚举属性(字符串键)
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
target[key] = deepClone(obj[key]);
}
}
// 拷贝自有 symbol 属性(含 Symbol.toPrimitive)
const symbols = Object.getOwnPropertySymbols(obj);
for (const sym of symbols) {
const desc = Object.getOwnPropertyDescriptor(obj, sym);
if (desc) {
Object.defineProperty(target, sym, desc);
}
}
return target;
}
// 使用示例
const src = {
[Symbol.toPrimitive](hint) {
return hint === 'number' ? 42 : 'hello';
},
name: 'test',
nested: { x: 1 }
};
src[Symbol('custom')] = 'extra';
const cloned = deepClone(src);
console.log(cloned[Symbol.toPrimitive]('string')); // 'hello'
console.log(cloned.name); // 'test'
console.log(cloned.nested.x); // 1
注意事项与边界情况
需明确几点限制:
-
Symbol.toPrimitive是方法,不是数据,深拷贝不改变其执行逻辑——它仍访问原闭包或 this(若为普通函数); - 若原对象通过
Object.defineProperty将toPrimitive设为不可配置/不可写,拷贝后新对象默认也会继承这些特性; - 循环引用、Map/Set/Date/RegExp 等需额外处理,symbol 支持只是其中一环;
- 原型链上的
Symbol.toPrimitive不会被getOwnPropertySymbols捕获,如需继承,需手动遍历原型链(通常不推荐,易破坏封装)。
不复杂但容易忽略:symbol 属性必须显式抓取和定义,否则深拷贝后新对象将丢失自定义类型转换能力。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











