如何检查$rootScope.$on监听器是否已设置?避免重复添加无需辅助变量
Great question! This is a super common pain point when working with AngularJS event listeners—repeated calls to add a listener lead to multiple instances firing when the event triggers. Here are a few approaches to solve this without relying on auxiliary tracking variables in your service:
Solution 1: Use Event Namespaces (Recommended)
AngularJS supports event namespaces, which let you target specific listeners for removal. Instead of adding a generic listener for messageFromSiteToHost, attach a unique namespace (like hostServiceListener) to it. Before adding the new listener, you can remove any existing ones with that namespace—this ensures only one listener is active at a time.
Here's how to implement it in your hostService.addListener method:
hostService.addListener = function(action) { // Remove any existing listener tied to our namespace $rootScope.off('messageFromSiteToHost.hostServiceListener'); // Add the new listener with the namespace $rootScope.$on('messageFromSiteToHost.hostServiceListener', function(event, data) { action(data); }); };
This approach is clean, uses Angular's built-in features, and avoids extra variables. Even if addListener is called multiple times, each call replaces the existing listener instead of stacking new ones.
Solution 2: Check for Existing Listeners (Using Private API)
If you specifically want to check if the listener exists before adding it (rather than overwriting), you can access Angular's private $$listeners property on $rootScope. Important: This is a private API, so it might break in future Angular versions—use this cautiously.
Here's how you could check:
hostService.addListener = function(action) { // Check if any listeners exist for the target event const hasMatchingListener = $rootScope.$$listeners['messageFromSiteToHost']?.some(listener => { // This works only if `action` is a reusable function reference // Anonymous functions will fail this check (each is a unique reference) return listener.listener === action; }); if (!hasMatchingListener) { $rootScope.$on('messageFromSiteToHost', action); } };
A big caveat here: if action is an anonymous function created each time addListener is called, this check won't work. The namespace approach is far more reliable for that scenario.
Bonus: Single Deregistration Variable (If Flexible on "Auxiliary" Rule)
You mentioned avoiding auxiliary variables, but if you're open to a single, simple variable (not a complex tracking system), storing the deregistration function returned by $on is another solid pattern:
let deregisterListener; hostService.addListener = function(action) { // Remove existing listener if it exists if (deregisterListener) { deregisterListener(); } // Add new listener and save the deregistration function deregisterListener = $rootScope.$on('messageFromSiteToHost', function(event, data) { action(data); }); };
This uses Angular's native return value from $on, which is designed exactly for cleaning up listeners. It's a widely accepted pattern and keeps your code straightforward.
内容的提问来源于stack exchange,提问作者SoftTimur

