event对象不可浅拷贝,因其为只读、不可序列化的宿主对象,正确做法是提取所需字段构造轻量纯对象,避免强引用dom节点导致内存问题。

浅拷贝本身不适用于 Event 对象,因为 Event 是只读的、不可序列化的宿主对象,任何试图“拷贝”它的操作(如 Object.assign、展开运算符、JSON.stringify)都只会返回空对象、undefined 或抛错——根本不会产生有效副本,更谈不上内存悬挂。
Event 对象不能也不该被浅拷贝
浏览器生成的 Event 实例(如 click、input 事件中的 event 参数)具有以下特性:
- 是临时、一次性使用的运行时对象,生命周期绑定到当前事件循环阶段
- 内部状态(如
target、currentTarget、isTrusted、timeStamp)不可枚举、不可写,也无法通过普通 JS 方法复制 -
JSON.stringify(event)返回undefined;Object.assign({}, event)返回空对象{};{...event}同样丢失全部关键字段和方法
真正需要保留的是事件数据,不是 Event 对象本身
所谓“避免数据悬挂”,本质是防止意外持有对 DOM 节点、闭包或长生命周期上下文的强引用。正确做法是提取所需字段,构造轻量纯对象:
- 只取你需要的属性:
{ type: e.type, targetId: e.target?.id, value: e.target?.value, keyCode: e.key } - 避免直接保存
e.target或e.currentTarget—— 它们是 DOM 节点,可能拖住整个子树 - 若需后续使用节点信息,改用
e.target.id或e.target.dataset等字符串标识,而非节点引用
高频事件中更要克制“保存 event”行为
像 scroll、mousemove 这类每秒触发数十次的事件,每次把 event 塞进数组或闭包,极易造成内存堆积:
- 不要做:
events.push(e)、cache.lastEvent = e - 推荐做:节流 + 提取关键字段 + 即用即弃
例如:const snapshot = { type: e.type, x: e.clientX, y: e.clientY }; process(snapshot); - 如果必须暂存,用
WeakMap关联临时上下文(如对应 DOM 元素),避免强引用阻止 GC
特殊场景:跨任务传递事件信息
当需在异步回调(如 setTimeout、Promise.then)中访问事件数据时:
- 禁止传入原始
e,因其属性可能在异步执行时已失效(如e.target被移除) - 应在事件处理器内立即提取并固化所需值:
const { clientX, clientY, target } = e; setTimeout(() => use(clientX, clientY), 0); - 如需结构化数据,用
structuredClone处理纯数据对象(但注意它不支持Event,只支持你手动构造的轻量对象)











