@ngdoc method
@name $rootScope.Scope#$watchGroup
@kind function

@description
A variant of {@link ng.$rootScope.Scope#$watch $watch()} where it watches an array of `watchExpressions`.
If any one expression in the collection changes the `listener` is executed.

- The items in the `watchExpressions` array are observed via the standard `$watch` operation. Their return
values are examined for changes on every call to `$digest`.
- The `listener` is called whenever any expression in the `watchExpressions` array changes.

@param {Array.<string|Function(scope)>} watchExpressions Array of expressions that will be individually
watched using {@link ng.$rootScope.Scope#$watch $watch()}

@param {function(newValues, oldValues, scope)} listener Callback called whenever the return value of any
expression in `watchExpressions` changes
The `newValues` array contains the current values of the `watchExpressions`, with the indexes matching
those of `watchExpression`
and the `oldValues` array contains the previous values of the `watchExpressions`, with the indexes matching
those of `watchExpression`
The `scope` refers to the current scope.
@returns {function()} Returns a de-registration function for all listeners.
function(watchExpressions, listener) {
        var oldValues = new Array(watchExpressions.length);
        var newValues = new Array(watchExpressions.length);
        var deregisterFns = [];
        var self = this;
        var changeReactionScheduled = false;
        var firstRun = true;

        if (!watchExpressions.length) {
          // No expressions means we call the listener ASAP
          var shouldCall = true;
          self.$evalAsync(function() {
            if (shouldCall) listener(newValues, newValues, self);
          });
          return function deregisterWatchGroup() {
            shouldCall = false;
          };
        }

        if (watchExpressions.length === 1) {
          // Special case size of one
          return this.$watch(watchExpressions[0], function watchGroupAction(value, oldValue, scope) {
            newValues[0] = value;
            oldValues[0] = oldValue;
            listener(newValues, (value === oldValue) ? newValues : oldValues, scope);
          });
        }

        forEach(watchExpressions, function(expr, i) {
          var unwatchFn = self.$watch(expr, function watchGroupSubAction(value) {
            newValues[i] = value;
            if (!changeReactionScheduled) {
              changeReactionScheduled = true;
              self.$evalAsync(watchGroupAction);
            }
          });
          deregisterFns.push(unwatchFn);
        });

        function watchGroupAction() {
          changeReactionScheduled = false;

          try {
            if (firstRun) {
              firstRun = false;
              listener(newValues, newValues, self);
            } else {
              listener(newValues, oldValues, self);
            }
          } finally {
            for (var i = 0; i < watchExpressions.length; i++) {
              oldValues[i] = newValues[i];
            }
          }
        }

        return function deregisterWatchGroup() {
          while (deregisterFns.length) {
            deregisterFns.shift()();
          }
        };
      }