megawac
megawac

Reputation: 11383

JavaScript instance level decorators

Is it possible to define a decorator that would be exposed at an instance level in the current iteration of the proposal

For example

function once(target, name, descriptor) {
  let {get} = descriptor;
  var called = false, result;
  if (typeof get === "function") {
    descriptor.get = function() {
      console.log(called);
      if (!called) {
          result = get.apply(this, arguments);
          called = true;
      }
      return result;
   }
  }
  return descriptor;
};

class X {
    @once
    get y() {
      return Math.random();
    }
}

var x1 = new X;
var x2 = new X;


// Should be false
console.log(x1.y !== x2.y)

I'm wanting behaviour similar to doing

class X2 {
  constructor() {
     // Set descriptor manually for this instance
    let descriptor = Object.getOwnPropertyDescriptor(X2.prototype, 'y');
    let d = Object.assign({}, descriptor);
    once(X, "y", d);
    Object.defineProperty(this, 'y', d);
  }
  get y() {
    return Math.random();
  }
}

Babel REPL example

Upvotes: 6

Views: 1460

Answers (1)

loganfsmyth
loganfsmyth

Reputation: 161677

Decorators only run once when the class is defined, but that doesn't mean that the definition-time logic can't set up the function to do stuff per-instance. In this case, you could memoize the value by defining an instance-specific property inside the prototye-level getter;

function once(target, name, descriptor) {
  let {get, writable, enumerable} = descriptor;
  if (typeof get === "function") {
    descriptor.get = function() {
      const value = get.apply(this, arguments);
      Object.defineProperty(this, name, {
        value,
        writable,
        enumerable
      });

      return value;
   }
  }
  return descriptor;
};

Upvotes: 5

Related Questions