能否完美克隆任意普通JS对象?RegExp克隆异常原因探究
关于cloneDeep中RegExp对象复制问题的解答
结论
你的第二个猜想正确:RegExp属于普通对象,你遇到的问题来自未被复制的专属内部槽,持有内部槽从来不是异域对象的专属特征。
核心概念澄清
先明确规范里两个定义的判定边界,你之前的误解本质是把两个独立的概念做了错误绑定:
- 判定异域对象的唯一标准是:对象的内部方法(包括
[[Get]]/[[Set]]/[[OwnPropertyKeys]]等共11个基础内部方法)是否偏离了普通对象的默认实现逻辑。只要所有内部方法都走默认实现,哪怕对象带了一堆专属内部状态,也属于普通对象。 - 内部槽是规范用来存储对象内部状态的抽象载体,和对象是不是异域对象没有关联,普通对象、异域对象都可以持有专属内部槽,且绝大多数内置构造函数生成的对象,都带有无法通过JS代码直接读写的内部槽。
RegExp问题的具体原因
- 你遇到的
source访问报错,本质是RegExp.prototype.source这个getter在执行逻辑时,会先校验this是否持有[[RegExpMatcher]]内部槽——这个槽存储了正则编译后的匹配逻辑、原始模式串、标志位等核心状态,你通过复制自有属性+绑定原型生成的对象,根本没有初始化这个内部槽,自然会触发类型错误。 RegExp没有被归入异域对象的原因非常直接:它的所有基础内部方法都完全遵循普通对象的默认实现,没有任何自定义的特殊逻辑,完全符合普通对象的判定标准。
cloneDeep场景的处理方案
不要尝试通过复制属性的方式伪造RegExp实例,直接调用构造函数生成新实例即可,参考实现:
function cloneRegExp(source) { const cloned = new RegExp(source.source, source.flags); cloned.lastIndex = source.lastIndex; return cloned; }
同类问题提示:除了RegExp之外,Date也是典型的持有专属内部槽的普通对象,核心槽为[[DateValue]],存储毫秒级时间戳,靠复制属性+挂原型的方式同样无法正确克隆,处理逻辑和RegExp一致,直接调用new Date(source.getTime())生成新实例即可。
内容的提问来源于stack exchange,提问作者Anurag Kalia
相关产品推荐
相关产品推荐

