0

How do I use decorators without having access to object.defineProperty?

I am looking into the shims available:

but in case those don't pass testing, is there another way decorators were intended to work?

I am using the decorator for $onRootScope.

I am using angular 1.08. I need compatibility with IE7.

Update

I have tried out a few methods that seem to work but I don't know the difference between them: plunkr

var app = angular.module('plunker', []);

app.config(['$provide', function($provide){
  $provide.decorator('$rootScope', ['$delegate', function($delegate){
    $delegate.a = 1;
    $delegate.constructor.prototype.b = 2;
    Object.defineProperty($delegate.constructor.prototype, 'c', {
      value: 3
    });
    return $delegate;
  }]);
}]);

app.controller('MainCtrl', function($rootScope, $scope) {
  console.log($rootScope);   //reveals `a` property
  console.log($rootScope.constructor.prototype); //=> {b:2, c:3}
  console.log($rootScope.a); //=> 1
  console.log($rootScope.b); //=> 2
  console.log($rootScope.c); //=> 3
  $scope.name = 'World';
});

Thank You.

Community
  • 1
  • 1
km6zla
  • 4,787
  • 2
  • 29
  • 51
  • 1
    Can yo give an example on why do you think you are forced to use Object.defineProperty to create a decorator over $onRootScope? – Edwin Dalorzo Feb 27 '14 at 23:43
  • @EdwinDalorzo It's in the question already. I referred to an answer on how to define $onRootScope and it uses `object.defineProperty()`. Other examples I have found also use that. Do you have an example that does *not* use `object.defineProperty()`? – km6zla Feb 28 '14 at 04:39

1 Answers1

0

Well, an equivalent solution to the pice of code you shared is:

var proto = Object.getPrototypeOf(Object.getPrototypeOf($delegate));
proto['$onRootScope'] = function (name, listener) {
   var unsubscribe = $delegate.$on(name, listener);
   this.$on('$destroy', unsubscribe);
};

In the original code this line $delegate.constructor.prototype is getting access to the $delegate's prototype prototype.

Then, once you get access to it, you can simply define a new function in it. You do not need to use defineProperty. The only caveat is that by using defineProperty you can configure that the method is not enumerable (should not appear in for-each loops). In this other way, the method added would appear in for-each loops. It may not be a problem for you though.

I have created a JSFiddle for this.

You can use John Resig's polyfill for getObjectPrototypeOfif the function is not available for your current browser:

if ( typeof Object.getPrototypeOf !== "function" ) {
  if ( typeof "test".__proto__ === "object" ) {
    Object.getPrototypeOf = function(object){
      return object.__proto__;
    };
  } else {
    Object.getPrototypeOf = function(object){
      // May break if the constructor has been tampered with
      return object.constructor.prototype;
    };
  }
}
Edwin Dalorzo
  • 76,803
  • 25
  • 144
  • 205
  • Unfortunately this suffers from the same [compatibility issue](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getPrototypeOf) as `object.defineProperty`. But it is interesting that you just used simple function assignment to provide the `$onRootScope` method. To get the prototype, could I just do `$delegate.prototype.prototype` or `$delegate.constructor.prototype.constructor.prototype` or something? – km6zla Feb 28 '14 at 17:32
  • Or can I just do `$delgate.constructor.prototype = { value: ... }`? – km6zla Feb 28 '14 at 17:41
  • @ogc-nick You could use a [John Resig's polyfill](http://ejohn.org/blog/objectgetprototypeof/) for the implementation of `getPrototypeOf`. I have edited my answer. – Edwin Dalorzo Feb 28 '14 at 18:04
  • I think you slightly misunderstood my question but answered it anyways by assigning a function to `proto['$onRootScope']`. – km6zla Feb 28 '14 at 18:04