
本文讲解为何直接修改 eventsource.prototype.onmessage 会触发 “illegal invocation” 错误,并提供兼容、可靠的方式(使用 addeventlistener)来无侵入地监听所有 sse 消息,同时不影响原有 onmessage 行为。
本文讲解为何直接修改 eventsource.prototype.onmessage 会触发 “illegal invocation” 错误,并提供兼容、可靠的方式(使用 addeventlistener)来无侵入地监听所有 sse 消息,同时不影响原有 onmessage 行为。
在开发浏览器扩展或调试工具时,常需对原生 API(如 EventSource)进行增强,例如在每次收到服务器发送事件(SSE)时自动打印日志。但若采用直接覆盖原型上的 onmessage setter(如 Object.getPrototypeOf(ori).onmessage = ...),将触发 Uncaught TypeError: Illegal invocation —— 这并非代码语法错误,而是 JavaScript 引擎对宿主对象(host object)内部方法调用上下文的严格校验所致。
根本原因在于:EventSource.prototype.onmessage 是一个访问器属性(accessor property),其 setter 内部依赖于 this 必须是有效的 EventSource 实例(即 receiver 必须为实例对象)。而 Object.getPrototypeOf(ori).onmessage = ... 的赋值操作发生在 EventSource.prototype 上,此时 this 指向的是原型对象而非实例,违反了底层实现的调用约束,因此抛出非法调用异常。
✅ 正确做法是利用标准事件机制:在实例上通过 addEventListener('message', ...) 注册额外监听器。该方式完全符合规范,不干扰用户后续对 source.onmessage 的赋值,也避免了 this 绑定问题。
以下是修复后的完整示例代码:
<script>
if (typeof EventSource !== "undefined") {
const OriginalEventSource = window.EventSource;
// 安全重写 EventSource 构造函数
window.EventSource = function EventSource(url, options) {
// 注意:需透传 options 参数以保持兼容性
const instance = new OriginalEventSource(url, options);
// 使用 addEventListener 添加调试日志,不影响用户 onmessage 设置
instance.addEventListener('message', function (event) {
console.log('[SSE Debug] Received message:', event.data, 'from', event.target.url);
});
return instance;
};
// 用户代码照常使用(完全无感知)
const source = new EventSource("demo_sse.php");
source.onmessage = function (event) {
document.getElementById("result").innerHTML += event.data + "<br>";
};
source.onerror = function (err) {
console.error("[SSE Error]", err);
};
} else {
document.getElementById("result").innerHTML = "Sorry, your browser does not support server-sent events...";
}
</script>? 关键注意事项:
- ✅ 务必在
new OriginalEventSource(...)后立即对返回的实例调用addEventListener,而非操作原型; - ✅ 若原构造函数支持第二个参数
options(如{ withCredentials: true }),重写时需透传,否则可能破坏功能; - ⚠️ 避免在重写中覆盖
instance.onmessage或instance.onerror——这会与用户后续赋值冲突;addEventListener是唯一推荐的多监听器方案; - ? 日志中建议包含
event.target.url和event.data,便于快速定位消息来源与内容。
总结:重写 Web API 时,应优先选择基于标准事件模型(addEventListener)的增强方式,而非尝试篡改原型访问器。它既保证了行为兼容性,又规避了宿主对象的调用上下文限制,是浏览器扩展与前端监控工具中的最佳实践。

















