即使我引用该函数,JavaScript函数也会被事件监听器多次调用

时间:2016-09-23 00:27:37

标签: javascript angularjs typescript event-listener

每次创建角度控制器时,我都会添加一个事件监听器。每次我离开这个页面并返回它时,都会添加一个新的事件监听器,因为再次调用构造函数。

当这个事件被触发时,相同的事件被调用两次,如果我离开并返回,它会被调用3次......等等。我只希望它总是被调用一次。

以下是添加事件侦听器的代码,以及它调用的Listener函数: (仅供参考我使用的是TypeScript)

在构造函数中:

this.$window.addEventListener("message", this.processApi, false);

被调用的函数:

processApi = (e) => {
    this.processApiMessage(e.data);
};

我读到我应该调用一个函数的引用而不是键入函数本身,所以它们都引用了一个函数的同一个实例,但同一个事件监听器被多次调用。

当我在chrome中使用开发人员工具,并转到EventListners,并转到消息部分时,每次点击构造函数时都会看到一个新的Window元素。我可以通过开发人员工具删除每个EventListers,但是当我这样做时似乎无法通过代码来完成它:

this.$window.removeEventListener("message", this.processApi, false);

我发现如果我刷新页面,所有的事件监听器都会清除,而构造函数中的那个会被创建,所以它可以正常工作。

我正在使用angular,并使用$ location服务导航到击中我的控制器的url,因此快速解决方法是将$ location.url(“url”)替换为window.location.href(“ URL“)

这似乎有效,因为当我导航到页面时页面会刷新。我宁愿保留$ location用于路由,并且只有一次我的事件监听器被命中,即使角度构造函数被多次命中。

1 个答案:

答案 0 :(得分:3)

当范围为$destroyed时,您需要删除事件侦听器。因此,在控制器构造函数中,您需要注入$scope对象。在构造函数中,执行以下操作:

$scope.$on('$destroy', () => 
  $window.removeEventListener("message", this.processApi));

可以肯定的是,有几种方法可以创建控制器。最常见的一个(特别是如果你使用的是TypeScript)是为控制器创建一个类。

我还会考虑将监听器添加到rootScope而不是$ window,这更像是Angular的处理方式。总而言之,它看起来像这样:

class MyController {
  constructor($rootScope, $scope) {
    'ngInject';

    let unsubscriber = $rootScope.$on('message', this.processApi, false);    
    $scope.$on('$destroy', () => unsubscriber());  
  }
  ...
}