function deepCompare(actual, expected, comparator, anyPropertyKey, matchAgainstAnyProp, dontMatchWholeObject) {
  var actualType = getTypeForFilter(actual);
  var expectedType = getTypeForFilter(expected);

  if ((expectedType === 'string') && (expected.charAt(0) === '!')) {
    return !deepCompare(actual, expected.substring(1), comparator, anyPropertyKey, matchAgainstAnyProp);
  } else if (isArray(actual)) {
    // In case `actual` is an array, consider it a match
    // if ANY of it's items matches `expected`
    return actual.some(function(item) {
      return deepCompare(item, expected, comparator, anyPropertyKey, matchAgainstAnyProp);
    });
  }

  switch (actualType) {
    case 'object':
      var key;
      if (matchAgainstAnyProp) {
        for (key in actual) {
          // Under certain, rare, circumstances, key may not be a string and `charAt` will be undefined
          // See: https://github.com/angular/angular.js/issues/15644
          if (key.charAt && (key.charAt(0) !== '$') &&
              deepCompare(actual[key], expected, comparator, anyPropertyKey, true)) {
            return true;
          }
        }
        return dontMatchWholeObject ? false : deepCompare(actual, expected, comparator, anyPropertyKey, false);
      } else if (expectedType === 'object') {
        for (key in expected) {
          var expectedVal = expected[key];
          if (isFunction(expectedVal) || isUndefined(expectedVal)) {
            continue;
          }

          var matchAnyProperty = key === anyPropertyKey;
          var actualVal = matchAnyProperty ? actual : actual[key];
          if (!deepCompare(actualVal, expectedVal, comparator, anyPropertyKey, matchAnyProperty, matchAnyProperty)) {
            return false;
          }
        }
        return true;
      } else {
        return comparator(actual, expected);
      }
    case 'function':
      return false;
    default:
      return comparator(actual, expected);
  }
}
@ngdoc directive
@name textarea
@restrict E

@description
HTML textarea element control with AngularJS data-binding. The data-binding and validation
properties of this element are exactly the same as those of the
{@link ng.directive:input input element}.

@param {string} ngModel Assignable AngularJS expression to data-bind to.
@param {string=} name Property name of the form under which the control is published.
@param {string=} required Sets `required` validation error key if the value is not entered.
@param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
   the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
   `required` when you want to data-bind to the `required` attribute.
@param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
   minlength.
@param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
   maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
   length.
@param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
   does not match a RegExp found by evaluating the AngularJS expression given in the attribute value.
   If the expression evaluates to a RegExp object, then this is used directly.
   If the expression evaluates to a string, then it will be converted to a RegExp
   after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
   `new RegExp('^abc$')`.<br />
   **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
   start at the index of the last search's match, thus not taking the whole input value into
   account.
@param {string=} ngChange AngularJS expression to be executed when input changes due to user
   interaction with the input element.
@param {boolean=} [ngTrim=true] If set to false AngularJS will not automatically trim the input.

@knownIssue

When specifying the `placeholder` attribute of `<textarea>`, Internet Explorer will temporarily
insert the placeholder value as the textarea's content. If the placeholder value contains
interpolation (`{{ ... }}`), an error will be logged in the console when AngularJS tries to update
the value of the by-then-removed text node. This doesn't affect the functionality of the
textarea, but can be undesirable.

You can work around this Internet Explorer issue by using `ng-attr-placeholder` instead of
`placeholder` on textareas, whenever you need interpolation in the placeholder value. You can
find more details on `ngAttr` in the
[Interpolation](guide/interpolation#-ngattr-for-binding-to-arbitrary-attributes) section of the
Developer Guide.