code: function(key, fn) {
          var attrs = this,
              $$observers = (attrs.$$observers || (attrs.$$observers = createMap())),
              listeners = ($$observers[key] || ($$observers[key] = []));
  
          listeners.push(fn);
          $rootScope.$evalAsync(function() {
            if (!listeners.$$inter && attrs.hasOwnProperty(key) && !isUndefined(attrs[key])) {
              // no one registered attribute interpolation function, so lets call it manually
              fn(attrs[key]);
            }
          });
  
          return function() {
            arrayRemove(listeners, fn);
          };
        }
docs: @ngdoc method
  @name $compile.directive.Attributes#$observe
  @kind function
  
  @description
  Observes an interpolated attribute.
  
  The observer function will be invoked once during the next `$digest` following
  compilation. The observer is then invoked whenever the interpolated value
  changes.
  
  @param {string} key Normalized key. (ie ngAttribute) .
  @param {function(interpolatedValue)} fn Function that will be called whenever
  the interpolated value of the attribute changes.
  See the {@link guide/interpolation#how-text-and-attribute-bindings-work Interpolation
  guide} for more info.
  @returns {function()} Returns a deregistration function for this observer.