JavaScript单例模式报错:route_socket.getInstance不是函数
解决“Uncaught TypeError: route_socket.getInstance is not a function”问题
问题核心原因
你遇到的错误本质是AMD规范的require函数是异步执行的:它的返回值并不是你在回调函数中返回的那个包含getInstance方法的对象。当你在require外部直接调用route_socket.getInstance()时,依赖还未加载完成,回调函数也没执行,此时route_socket只是AMD加载器的内部处理对象,自然没有getInstance方法。
解决方案
方案1:将单例调用逻辑放入require回调内部
既然依赖加载是异步的,所有依赖RouteSocket的代码都应该放在require的回调函数里,确保依赖加载完成后再执行:
require([ "esri/WebMap", "esri/views/MapView", "esri/layers/RouteLayer", "esri/rest/support/Stop" ], function(WebMap, MapView, RouteLayer, Stop) { var instance; class RouteSocket { #apiKey; constructor() { this.#apiKey = token.getAttribute("token"); this.routeUrl = "https://route-api.arcgis.com/arcgis/rest/services/World/Route/NAServer/Route_World"; this.churches = []; this.stompClient = null; this.view = null; this.connect(); this.origin = new Stop({ geometry: { x: OX, y: OY }, name: "Origin" }) this.destination = new Stop({ geometry: { x: DX, y: DY }, name: "Destination" }) this.addRandomChurch(); this.addRandomChurch(); this.addRandomChurch(); } addRandomChurch() { var randomX = ((OX - DX) * Math.random()) + OX; var randomY = ((OY - DY) * Math.random()) + OY; var ChurchStop = { x: randomX, y: randomY, name: "Church" }; this.churchAdded(ChurchStop); } setView(view) { this.view = view; console.log("We have set the view"); this.churches.forEach(church => this.addRoute(church)); } connect() { var socket = new SockJS('/ws'); this.stompClient = Stomp.over(socket); this.stompClient.connect({}, this.onConnected.bind(this), this.onError.bind(this)); } async addRoute(church) { let stops = [ this.origin, church, this.destination ]; let routeLayer = new RouteLayer({ stops }); this.view.map.add(routeLayer); await routeLayer.load(); const results = await routeLayer.solve({ apiKey: this.#apiKey }); routeLayer.update(results); } onConnected() { this.stompClient.subscribe('/topic/map/' + hobnobID.getAttribute("hobnobID"), this.onMessageReceived.bind(this)); synchroniseDataMapSocket.establishSocket(this); } onMessageReceived(payload) { var message = JSON.parse(payload.body); var ChurchStop = { x: message.X, y: message.Y, name: "Church" }; this.churchAdded(ChurchStop); } churchAdded(ChurchStop) { if (!this.view) { this.churches.push(ChurchStop); return; } else { this.addRoute(ChurchStop); } } }; function createInstance() { var object = new RouteSocket(); return object; } // 在这里获取并使用单例实例 const routeSocketInstance = (function() { if (!instance) { instance = createInstance(); } return instance; })(); // 后续需要操作单例的代码都放在此处 // 示例:routeSocketInstance.setView(yourMapView); });
方案2:用Promise封装AMD加载(多场景复用)
如果需要在多个地方复用这个单例,可以把AMD加载逻辑封装成Promise,通过异步方式获取实例:
// 封装单例获取逻辑为Promise const getRouteSocketInstance = () => { return new Promise((resolve) => { require([ "esri/WebMap", "esri/views/MapView", "esri/layers/RouteLayer", "esri/rest/support/Stop" ], function(WebMap, MapView, RouteLayer, Stop) { var instance; class RouteSocket { #apiKey; constructor() { this.#apiKey = token.getAttribute("token"); this.routeUrl = "https://route-api.arcgis.com/arcgis/rest/services/World/Route/NAServer/Route_World"; this.churches = []; this.stompClient = null; this.view = null; this.connect(); this.origin = new Stop({ geometry: { x: OX, y: OY }, name: "Origin" }) this.destination = new Stop({ geometry: { x: DX, y: DY }, name: "Destination" }) this.addRandomChurch(); this.addRandomChurch(); this.addRandomChurch(); } // 其余方法保持不变... addRandomChurch() { var randomX = ((OX - DX) * Math.random()) + OX; var randomY = ((OY - DY) * Math.random()) + OY; var ChurchStop = { x: randomX, y: randomY, name: "Church" }; this.churchAdded(ChurchStop); } setView(view) { this.view = view; console.log("We have set the view"); this.churches.forEach(church => this.addRoute(church)); } connect() { var socket = new SockJS('/ws'); this.stompClient = Stomp.over(socket); this.stompClient.connect({}, this.onConnected.bind(this), this.onError.bind(this)); } async addRoute(church) { let stops = [ this.origin, church, this.destination ]; let routeLayer = new RouteLayer({ stops }); this.view.map.add(routeLayer); await routeLayer.load(); const results = await routeLayer.solve({ apiKey: this.#apiKey }); routeLayer.update(results); } onConnected() { this.stompClient.subscribe('/topic/map/' + hobnobID.getAttribute("hobnobID"), this.onMessageReceived.bind(this)); synchroniseDataMapSocket.establishSocket(this); } onMessageReceived(payload) { var message = JSON.parse(payload.body); var ChurchStop = { x: message.X, y: message.Y, name: "Church" }; this.churchAdded(ChurchStop); } churchAdded(ChurchStop) { if (!this.view) { this.churches.push(ChurchStop); return; } else { this.addRoute(ChurchStop); } } }; function createInstance() { var object = new RouteSocket(); return object; } if (!instance) { instance = createInstance(); } resolve(instance); }); }); }; // 使用时通过Promise获取实例 getRouteSocketInstance().then(instance => { console.log("单例实例已获取:", instance); // 示例:instance.setView(yourMapView); });
额外注意事项
- 确保全局变量
token、OX、OY、DX、DY、hobnobID、SockJS、Stomp、synchroniseDataMapSocket在代码执行时已正确定义,否则会引发新的错误。 - 两种方案都保留了单例模式的核心逻辑:确保
RouteSocket实例只被创建一次。
内容的提问来源于stack exchange,提问作者Drivers Sea
相关产品推荐
相关产品推荐

