浏览器运行WebSocket类代码报错:无法读取未声明的私有成员#serverSocket
解决私有成员
#serverSocket读取错误问题 问题描述
运行下述ControlPanel类代码时,在#monitorConnection()方法内触发错误:
Cannot read private member #serverSocket from an object whose class did not declare it
原代码:
class ControlPanel { #serverSocket; constructor() { this.#connectToWebSocketServer(); this.#monitorConnection(); console.log("ControlPanel::new"); } #connectToWebSocketServer() { this.#serverSocket = new WebSocket(window.location.toString().startsWith('https') ? window.location.toString().replace("https", "wss") : window.location.toString().replace("http", "wss") ); this.#serverSocket.onclose = this.#onWebSocketClose; this.#serverSocket.onerror = this.#onWebSocketError; this.#serverSocket.onmessage = this.#onWebSocketMessage; this.#serverSocket.onopen = this.#onWebSocketOpen; console.log("ControlPanel::#connectToWebSocketServer"); } #monitorConnection() { setInterval(function() { if(this.#serverSocket.readyState != 1) this.#connectToWebSocketServer() }, 1500); } #onWebSocketClose(event) { console.debug("ControlPanel::#onWebSocketClose ", event); } #onWebSocketError(event) { console.debug("ControlPanel::#onWebSocketError ", event); } #onWebSocketMessage(event) { console.log("ControlPanel::#onWebSocketMessage ", event); } #onWebSocketOpen(event) { console.debug("ControlPanel::#onWebSocketOpen ", event); } } const controlPanel = new slim.generator.ControlPanel();
此外,将#onWebSocketOpen方法更新为以下代码后,WebSocket首次建立连接时也会触发相同错误:
#onWebSocketOpen(event) { console.debug("ControlPanel::#onWebSocketOpen ", event); this.#serverSocket.send(); }
问题根源
两处错误的核心都是**this指向不对**:
setInterval里用普通function当回调,此时this指向浏览器全局对象window,不是ControlPanel的实例,自然读不到类的私有成员#serverSocket- 给WebSocket绑定事件时,直接赋值
this.#onWebSocketOpen这种函数引用,触发事件时this会指向WebSocket实例本身,同样不是ControlPanel实例,无法访问私有成员
修复方法
1. 修复定时器回调的this指向
把setInterval的普通函数改成箭头函数,箭头函数会继承外层作用域的this,也就是ControlPanel实例:
#monitorConnection() { setInterval(() => { if(this.#serverSocket.readyState !== 1) this.#connectToWebSocketServer() }, 1500); }
2. 修复WebSocket事件的this指向
有两种常用方式:
方式一:用bind绑定实例this
在绑定事件时,给每个事件处理函数绑定当前实例的this:
#connectToWebSocketServer() { this.#serverSocket = new WebSocket(window.location.toString().startsWith('https') ? window.location.toString().replace("https", "wss") : window.location.toString().replace("http", "wss") ); this.#serverSocket.onclose = this.#onWebSocketClose.bind(this); this.#serverSocket.onerror = this.#onWebSocketError.bind(this); this.#serverSocket.onmessage = this.#onWebSocketMessage.bind(this); this.#serverSocket.onopen = this.#onWebSocketOpen.bind(this); console.log("ControlPanel::#connectToWebSocketServer"); }
方式二:用箭头函数包裹事件处理函数
通过箭头函数间接调用原方法,确保this指向ControlPanel实例:
#connectToWebSocketServer() { this.#serverSocket = new WebSocket(window.location.toString().startsWith('https') ? window.location.toString().replace("https", "wss") : window.location.toString().replace("http", "wss") ); this.#serverSocket.onclose = (event) => this.#onWebSocketClose(event); this.#serverSocket.onerror = (event) => this.#onWebSocketError(event); this.#serverSocket.onmessage = (event) => this.#onWebSocketMessage(event); this.#serverSocket.onopen = (event) => this.#onWebSocketOpen(event); console.log("ControlPanel::#connectToWebSocketServer"); }
做完以上修改后,两处场景的this都会正确指向ControlPanel实例,就能正常访问私有成员#serverSocket了。
内容的提问来源于stack exchange,提问作者Jeff Greer
相关产品推荐
相关产品推荐

