【问题标题】:How to watch a function return value in JavaScript如何在 JavaScript 中查看函数返回值
【发布时间】:2014-04-07 05:40:43
【问题描述】:

在浏览器中,对于一个对象,一个属性有它的getter 和setter。我们可以覆盖它们来观察属性。函数的返回值如何? 在 AngularJS 中,使用 'ng-show' 来控制组件的可见性。可以这样写:

<ANY ng-show="aFunction()"> ... </ANY>

函数可以定义为:

var tmp = false;
$scope.aFunction = function() { return tmp; };

如果在运行时,'tmp' 更改为 true, 将显示。 AngularJS 如何知道返回值发生了变化?有没有定时检查变化的超时功能?

【问题讨论】:

    标签: javascript angularjs function watch


    【解决方案1】:

    没有超时功能。有一个 AngularJS 特有的概念,叫做“digest-cycle”。
    您可以从文档中了解有关 scope life-cycle 的更多信息。
    我还发现 this article 对于介绍这种机制非常有用。
    最后,我相信这里有很多相关的答案。

    一般来说,您应该尝试找出函数$apply()、$digest()、$watch() 的作用。

    【讨论】:

    • 谢谢,很有帮助~:D
    【解决方案2】:

    没有魔法或超时,Angular 理解某些东西(包括函数返回值)改变的方式是在每次摘要循环运行时执行 dirty-checking。要了解它是如何工作的,让我们看一下您的示例,看看 Angular 为您执行的步骤,以及您如何告诉 Angular 发生了一些变化。

    第 1 步:创建手表

    如果我们查看ngShowDirective的源代码,我们会看到以下内容

    var ngShowDirective = ['$animate', function($animate) {
      return function(scope, element, attr) {
        scope.$watch(attr.ngShow, function ngShowWatchAction(value){ // <= create watch
          $animate[toBoolean(value) ? 'removeClass' : 'addClass'](element, 'ng-hide');
        });
      };
    }];
    

    scope.$watch() 的第一个参数是一个表达式,它取自 DOM 元素的 ng-show 属性,并编译为一个函数,该函数在监视值更改时返回 true。由于传递的是一个函数 - Angular 将运行它来获取新值。

    第 2 步:订阅 DOM 事件并运行摘要循环

    Angular 订阅了许多 DOM 事件,以便运行摘要循环并检查每次浏览器中发生某些事情时是否发生了某些变化。例如,让我们看看当浏览器触发 hashchange 事件时 Angular 做了什么:

    $browser.onUrlChange(function(newUrl) {
      if ($location.absUrl() != newUrl) {
        $rootScope.$evalAsync(function() {
          var oldUrl = $location.absUrl();
    
          $location.$$parse(newUrl);
          if ($rootScope.$broadcast('$locationChangeStart', newUrl,
                                    oldUrl).defaultPrevented) {
            $location.$$parse(oldUrl);
            $browser.url(oldUrl);
          } else {
            afterLocationChange(oldUrl);
          }
        });
        if (!$rootScope.$$phase) $rootScope.$digest(); // <= run digest loop if it is not already running
      }
    });
    

    第 3 步:运行所有手表并在手表发生变化时调用回调

    Scope.prototype = {
      ...
      $digest: function() {
        ...
        if ((value = watch.get(current)) !== (last = watch.last) &&
                        !(watch.eq
                            ? equals(value, last)
                            : (typeof value == 'number' && typeof last == 'number'
                               && isNaN(value) && isNaN(last)))) {
          ...
          watch.fn(value, ((last === initWatchVal) ? value : last), current); // <= run watch callback function in case watch value has been changed
          ...
        }
        ...
      }
    }
    

    就是这样,Angular 就是这样知道何时让&lt;ANY ng-show="aFunction()"&gt; ... &lt;/ANY&gt; 可见或不可见。

    在最常见的情况下,Angular 会为您完成所有这些工作,但是在某些情况下,Angular 并不知道某些事情发生了变化。例如,当值在自动创建手表的 Angular 范围之外更改时。在这些情况下,您应该注意自己致电scope.$digest() 或scope.$apply()。最常见的情况之一是在指令的事件侦听器中更改范围属性值:

    angular.module('demo').directive('clickable', function() {
      return {
        link: function(scope, element) {
          element.bind('click', function() {
            scope.$apply(function() { // <= call scope.$apply() in order to tell Angular that something is changed in the scope
              scope.foo++;
            });
          });
        }
      }
    });
    

    除了$apply()调用$digest()之外,推荐使用$apply()带回调函数。这样 Angular 将知道在范围更改期间是否有问题,并在出现任何错误时处理错误。

    希望它有助于理解 Angular 如何知道范围内的变化。

    在 Angular 之外观察函数返回值

    如果您想利用 Angular 在 Angular 应用程序代码之外观察函数返回值的功能,您可以执行以下操作:

    1. 获取应用程序的顶级元素
    2. 获取 Angular 的 $rootScope
    3. 使用$watch()观察变化

    示例:

    var valueToBeChanged = 0,
        rootElement = document.querySelector('[ng-app]'),
        rootScope = angular.element(rootElement).scope();
    
    function myFunc() {
      return valueToBeChanged;
    }
    
    function myCallback() {
      console.log('Value have been changed!');
    }
    
    rootScope.$watch(myFunc, myCallback);
    
    angular.element(document.querySelector('body')).on('click', function() {
      rootScope.$apply(function() {
        valueToBeChanged++;
      });
    });
    

    Plunker:http://plnkr.co/edit/LRK08EsP0jrOu9n42VwB?p=preview

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-05
      • 1970-01-01
      相关资源
      最近更新 更多