重写EventSource时触发'Uncaught TypeError: Illegal invocation'错误求助
在线演示地址
https://www.w3schools.com/html/tryit.asp?filename=tryhtml5_sse
问题描述
我尝试重写EventSource,实现每当onmessage触发时执行console.log输出日志。
代码示例
<!DOCTYPE html> <html> <body> <h1>Getting server updates</h1> <div id="result"></div> <script> if (typeof EventSource !== "undefined") { const original = EventSource; // part of chrome extension window.EventSource = function EventSource(url) { const ori = new original(url); Object.getPrototypeOf(ori).onmessage = function (event) { console.log(event); // log on every message ori.onmessage(event); }; return ori; }; // actual source code var source = new EventSource("demo_sse.php"); source.onmessage = function (event) { document.getElementById("result").innerHTML += event.data + "<br>"; }; } else { document.getElementById("result").innerHTML = "Sorry, your browser does not support server-sent events..."; } </script> </body> </html>
报错信息
VM461:10 Uncaught TypeError: 非法调用
at new EventSource (:10:42)
at :21:16
at submitTryit (tryit.asp?filename=tryhtml5_sse:853:17)
at HTMLButtonElement.onclick (tryit.asp?filename=tryhtml5_sse:755:133)
EventSource @ VM461:10
(anonymous) @ VM461:21
submitTryit @ tryit.asp?filename=tryhtml5_sse:853
onclick @ tryit.asp?filename=tryhtml5_sse:755
uic.js?v=1.0.5:1 Uncaught ReferenceError: adngin 未定义
at uic_r_p (uic.js?v=1.0.5:1:54492)
at HTMLButtonElement.onclick (tryit.asp?filename=tryhtml5_sse:755:148)
uic_r_p @ uic.js?v=1.0.5:1
onclick @ tryit.asp?filename=tryhtml5_sse:755
解决方案
你的代码存在两个核心问题:
- 非法调用错误:自定义的
EventSource构造函数未正确继承原生构造函数的原型链,导致浏览器无法识别其为合法的EventSource实例。 - 日志逻辑失效:直接修改
EventSource.prototype.onmessage会被后续设置的实例onmessage覆盖,且存在无限递归风险。
修复后的代码如下:
if (typeof EventSource !== "undefined") { const original = EventSource; // 自定义EventSource构造函数,保留原生功能并添加日志 window.EventSource = function(...args) { const instance = new original(...args); // 添加message事件监听器实现日志,不影响原有onmessage逻辑 instance.addEventListener('message', (event) => { console.log('SSE消息:', event); }); return instance; }; // 保留原生原型链,避免类型检查失败 Object.setPrototypeOf(window.EventSource, Object.getPrototypeOf(original)); window.EventSource.prototype = original.prototype; // 原有业务代码不变 var source = new EventSource("demo_sse.php"); source.onmessage = function (event) { document.getElementById("result").innerHTML += event.data + "<br>"; }; } else { document.getElementById("result").innerHTML = "抱歉,你的浏览器不支持服务器推送事件..."; }
修复说明:
- 使用
addEventListener监听message事件,既实现日志输出,又不会与业务代码的onmessage冲突,两者都会执行。 - 通过
Object.setPrototypeOf和原型赋值,确保自定义构造函数与原生EventSource保持一致的原型链,解决非法调用问题。 - 使用扩展运算符
...args接收所有构造参数,兼容原生EventSource的完整参数列表(如可选的options参数)。
内容的提问来源于stack exchange,提问作者rafl952

