如何统计页面上的手表总数?

有没有一种方法可以用JavaScript计算整个页面上的angular度表的数量?

我们使用Batarang ,但并不总是适合我们的需求。 我们的应用程序很大,我们有兴趣使用自动化testing来检查手表数量是否过多。

在每个控制器的基础上计数手表也是有用的。

编辑 :这是我的尝试。 它在所有具有ng级作用域的手表中统计着手表。

(function () { var elts = document.getElementsByClassName('ng-scope'); var watches = []; var visited_ids = {}; for (var i=0; i < elts.length; i++) { var scope = angular.element(elts[i]).scope(); if (scope.$id in visited_ids) continue; visited_ids[scope.$id] = true; watches.push.apply(watches, scope.$$watchers); } return watches.length; })(); 

(你可能需要将body改成html或者你把ng-app放在哪里)

 (function () { var root = angular.element(document.getElementsByTagName('body')); var watchers = []; var f = function (element) { angular.forEach(['$scope', '$isolateScope'], function (scopeProperty) { if (element.data() && element.data().hasOwnProperty(scopeProperty)) { angular.forEach(element.data()[scopeProperty].$$watchers, function (watcher) { watchers.push(watcher); }); } }); angular.forEach(element.children(), function (childElement) { f(angular.element(childElement)); }); }; f(root); // Remove duplicate watchers var watchersWithoutDuplicates = []; angular.forEach(watchers, function(item) { if(watchersWithoutDuplicates.indexOf(item) < 0) { watchersWithoutDuplicates.push(item); } }); console.log(watchersWithoutDuplicates.length); })(); 
  • 感谢erilem指出这个答案是缺less$isolateScopesearch和观察者可能在他/她的答案/评论中被复制。

  • 感谢Ben2307指出, 'body'可能需要改变。


原版的

我做了同样的事情,除了我检查了HTML元素的数据属性而不是它的类。 我在这里跑你的:

http://fluid.ie/

得到了83.我跑了我的121。

 (function () { var root = $(document.getElementsByTagName('body')); var watchers = []; var f = function (element) { if (element.data().hasOwnProperty('$scope')) { angular.forEach(element.data().$scope.$$watchers, function (watcher) { watchers.push(watcher); }); } angular.forEach(element.children(), function (childElement) { f($(childElement)); }); }; f(root); console.log(watchers.length); })(); 

我也把这个放在我的里面:

 for (var i = 0; i < watchers.length; i++) { for (var j = 0; j < watchers.length; j++) { if (i !== j && watchers[i] === watchers[j]) { console.log('here'); } } } 

而且没有任何东西可以打印出来,所以我猜测我的是更好的(因为它发现了更多的手表),但是我缺乏亲密的angular度知识来确定我的解决scheme不是真正的子集。

我认为所提到的方法是不准确的,因为它们将同一范围内的观察者数量加倍。 这是我的小书签的版本:

https://gist.github.com/DTFagus/3966db108a578f2eb00d

它还显示了一些分析观察者的更多细节。

在检查范围结构的基础上,我将这个解决scheme放在一起。 这“似乎”工作。 我不确定这是多么准确,这绝对取决于一些内部的API。 我正在使用angularjs 1.0.5。

  $rootScope.countWatchers = function () { var q = [$rootScope], watchers = 0, scope; while (q.length > 0) { scope = q.pop(); if (scope.$$watchers) { watchers += scope.$$watchers.length; } if (scope.$$childHead) { q.push(scope.$$childHead); } if (scope.$$nextSibling) { q.push(scope.$$nextSibling); } } window.console.log(watchers); }; 

有一个新的Chrome插件,可以随时在您的应用程序中自动显示当前总观察者和最后一次更改(+/-)…这真是太棒了。

https://chrome.google.com/webstore/detail/angular-watchers/nlmjblobloedpmkmmckeehnbfalnjnjk

最近我在应用程序中遇到了大量的观察者,我发现了一个很好的库,名为ng-statshttps://github.com/kentcdodds/ng-stats 。 它具有最小的设置,并为您提供当前页面+摘要周期长度的观察者数量。 它也可以投影一个小的实时图。

对于Jared的答案 , 词语稍有改进。

 (function () { var root = $(document.getElementsByTagName('body')); var watchers = 0; var f = function (element) { if (element.data().hasOwnProperty('$scope')) { watchers += (element.data().$scope.$$watchers || []).length; } angular.forEach(element.children(), function (childElement) { f($(childElement)); }); }; f(root); return watchers; })(); 

在AngularJS 1.3.2中, countWatchers方法被添加到ngMock模块中:

 / **
  * @ngdoc方法
  * @name $ rootScope.Scope#$ countWatchers
  * @module ngMock
  * @description
  *计算当前范围的所有直接和间接子范围的观察者。
  *
  *当前范围的观察者包括在计数中,所有观察者都包括在内
  *隔离子范围。
  *
  * @returns {number}观察者总数。
  * /

 函数countWatchers() 
    {
    var root = angular.element(document).injector()。get('$ rootScope');
    var count = root。$$ watchers? 根。$$ watchers.length:0;  //包含当前范围
    var pendingChildHeads = [root。$$ childHead];
    var currentScope;

    while(pendingChildHeads.length) 
     {
     currentScope = pendingChildHeads.shift();

    而(currentScope) 
       {
       count + = currentScope。$$观察者?  currentScope。$$ watchers.length:0;
       pendingChildHeads.push(currentScope $$ childHead。);
       currentScope = currentScope。$$ nextSibling;
       }
     }

   返回计数;
    }

参考

  • AngularJS Changelog:1.3.2

  • ngMock:$ rootScope.Scope。$ countWatchers

我直接从$digest函数本身获取下面的代码。 当然,您可能需要更新底部的应用程序元素select器( document.body )。

 (function ($rootScope) { var watchers, length, target, next, count = 0; var current = target = $rootScope; do { if ((watchers = current.$$watchers)) { count += watchers.length; } if (!(next = (current.$$childHead || (current !== target && current.$$nextSibling)))) { while (current !== target && !(next = current.$$nextSibling)) { current = current.$parent; } } } while ((current = next)); return count; })(angular.element(document.body).injector().get('$rootScope')); 

这是我使用的function:

 /** * @fileoverview This script provides a window.countWatchers function that * the number of Angular watchers in the page. * * You can do `countWatchers()` in a console to know the current number of * watchers. * * To display the number of watchers every 5 seconds in the console: * * setInterval(function(){console.log(countWatchers())}, 5000); */ (function () { var root = angular.element(document.getElementsByTagName('body')); var countWatchers_ = function(element, scopes, count) { var scope; scope = element.data().$scope; if (scope && !(scope.$id in scopes)) { scopes[scope.$id] = true; if (scope.$$watchers) { count += scope.$$watchers.length; } } scope = element.data().$isolateScope; if (scope && !(scope.$id in scopes)) { scopes[scope.$id] = true; if (scope.$$watchers) { count += scope.$$watchers.length; } } angular.forEach(element.children(), function (child) { count = countWatchers_(angular.element(child), scopes, count); }); return count; }; window.countWatchers = function() { return countWatchers_(root, {}, 0); }; })(); 

这个函数使用一个散列不是多次统一同一个作用域。

Lars Eidnes的博客http://larseidnes.com/2014/11/05/angularjs-the-bad-parts/发布了一个recursion函数来收集总的观察者数量。; 我使用这里发布的函数和他在他的博客中发布的函数比较了结果,该函数生成了稍高一些的数字。 我不知道哪一个更准确。 刚刚添加在这里作为一个参考。

 function getScopes(root) { var scopes = []; function traverse(scope) { scopes.push(scope); if (scope.$$nextSibling) traverse(scope.$$nextSibling); if (scope.$$childHead) traverse(scope.$$childHead); } traverse(root); return scopes; } var rootScope = angular.element(document.querySelectorAll("[ng-app]")).scope(); var scopes = getScopes(rootScope); var watcherLists = scopes.map(function(s) { return s.$$watchers; }); _.uniq(_.flatten(watcherLists)).length; 

注意:您可能需要将您的Angular应用程序的“ng-app”更改为“data-ng-app”。

Plantian的答案更快: https ://stackoverflow.com/a/18539624/258482

这是我手写的一个function。 我没有考虑使用recursion函数,但这是我所做的。 这可能会更精简,我不知道。

 var logScope; //put this somewhere in a global piece of code 

然后把它放在你的最高控制器(如果你使用全局控制器)。

 $scope.$on('logScope', function () { var target = $scope.$parent, current = target, next; var count = 0; var count1 = 0; var checks = {}; while(count1 < 10000){ //to prevent infinite loops, just in case count1++; if(current.$$watchers) count += current.$$watchers.length; //This if...else is also to prevent infinite loops. //The while loop could be set to true. if(!checks[current.$id]) checks[current.$id] = true; else { console.error('bad', current.$id, current); break; } if(current.$$childHead) current = current.$$childHead; else if(current.$$nextSibling) current = current.$$nextSibling; else if(current.$parent) { while(!current.$$nextSibling && current.$parent) current = current.$parent; if(current.$$nextSibling) current = current.$$nextSibling; else break; } else break; } //sort of by accident, count1 contains the number of scopes. console.log('watchers', count, count1); console.log('globalCtrl', $scope); }); logScope = function () { $scope.$broadcast('logScope'); }; 

最后是一个书店:

 javascript:logScope();