2

In ES5^ I can create a proxy object and bind it to the other functions constructors, see what I want to do in the example below:

function X() {
  this.x = `${this.anyAttr} it's a trap`;
}

function Y() {
  this.y = `${this.anyAttr} do the barrel roll`;
}

function Z(...a) {
  const proxy = new Proxy(this, { 
    get(target, key) {
      if (target[key]) {
        return Reflect.get(target, key);
      }
      return "FROM Z:";
    }
  });
  X.apply(proxy, a);
  Y.apply(proxy, a);

  return proxy;
}

const obj = new Z();

console.log(obj.someAttr) // FROM Z:
console.log(obj.x)        // FROM Z: it's a trap
console.log(obj.y)        // FROM Z: do the barrel roll

I want to do the same thing, but with the ES6 Class syntax, but the apply method can't be used with ES6 classes, see the example below:

class X {
    constructor() {
        this.x = `${this.anyAttr} it's a trap`
    }
}

class Y {
    constructor() {
        this.y = `${this.anyAttr} do the barrel roll`
    }
}

function Z(...a) {
    const proxy = new Proxy(this, { 
        get(target, key) {
             if (target[key])
                return Reflect.get(target, key)
             return "FROM Z:"
        }
    })

    X.apply(proxy, a) // Uncaught TypeError: Class constructor X cannot be invoked without 'new' at new Z
    Y.apply(proxy, a) // Uncaught TypeError: Class constructor Y cannot be invoked without 'new' at new Z
    
    return proxy;
}

const obj = new Z()

To be specific, I need to mix classes, but before construct the superclasses I need to link a proxy object to do some black magic, so...

Is there a way to do something similar to the first example but using ES6 Classes?

  • 1
    ES6 classes cannot be used as mixins. The proxy around `this` doesn't matter. – Bergi Feb 04 '21 at 17:51
  • 1
    Can you explain what you'd _actually_ want to use this for? Because I'm not sure there's a use-case for this kind of code in modern JS (that is, there might be a use case in terms of what you want to achieve, but that use case can almost certainly be better addressed with a different code solution). – Mike 'Pomax' Kamermans Feb 04 '21 at 18:39
  • Here X and Y are just examples, in my scenario X and Y will be written by other programmers, what I need to do is inject values and methods to replace the common ones without changing the previous written class... I can't just extend it because I need to ensure that the methods and values are overridden in the superclass before the constructor is called – Daniel de Andrade Varela Feb 04 '21 at 19:04
  • @DanieldeAndradeVarela See https://stackoverflow.com/q/30732241/1048572, https://stackoverflow.com/q/42247434/1048572 or https://stackoverflow.com/q/29879267/1048572 – Bergi Feb 04 '21 at 19:49

1 Answers1

2

I'd recommend re-designing your mixins to be higher-order classes like this:

const X = (Base) => class extends Base {
  constructor(...args) {
    super(...args);
    this.x = `${this.anyAttr} it's a trap`;
  }
};

const Y = (Base) => class extends Base {
  constructor(...args) {
    super(...args);
    this.y = `${this.anyAttr} do the barrel roll`;
  }
};

const Z = Y(X(class {
  constructor(...args) {
    return new Proxy(this, {
      get(target, key, receiver) {
        if (Reflect.has(target, key, receiver))
          return Reflect.get(target, key);
        return "FROM Z:";
      }
    });
  }
}));

const obj = new Z();

console.log(obj.someAttr); // FROM Z:
console.log(obj.x); // FROM Z: it's a trap
console.log(obj.y); // FROM Z: do the barrel roll
Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153
  • 1
    +1 for revealing a really interesting behavior of JavaScript to me. Can you really just manipulate a class as a value? – Isaac Corbrey Feb 04 '21 at 19:14
  • 1
    @IsaacCorbrey yes. This is what people mean when they say that functions (and by extension, classes) are "first-class objects" in JavaScript. – Patrick Roberts Feb 04 '21 at 19:16
  • 1
    I knew about functions, but since I've mainly used ES6 in my development career I always forget that classes are literally just syntactic sugar for functions with state – Isaac Corbrey Feb 04 '21 at 19:23
  • Hi @PatrickRoberts thanks for the suggestion, but in my case I will need to create something more dynamic, because the base classes are not written by me, so, consider the scenario, another programmer writes a class called OtherClass, and I need to create a superclass for OtherClass , but I don't know how to mixin it like this `class OtherClass extends MySuperClass`, the only thing I know how to do is` class MyClass extends OtherClass` ... you know what I mean? – Daniel de Andrade Varela Feb 04 '21 at 19:44
  • @DanieldeAndradeVarela instead of another programmer writing `class OtherClass ...` they need to write `const OtherClass = (Base) => class extends Base ...` with this approach. Then _you_ write `const MyClass = OtherClass(class ...)` instead of `class MyClass extends OtherClass ...` – Patrick Roberts Feb 04 '21 at 20:24
  • unfortunately I cannot demand the other team to write that way, so i'm trying to find some workaround solution – Daniel de Andrade Varela Feb 04 '21 at 20:40
  • @DanieldeAndradeVarela you can't delegate construction to an ES6 class and specify a `this` the way you're trying to do, it's not possible. The approach I'm suggesting is a fairly canonical way to define mixins, check out [this article](https://justinfagnani.com/2015/12/21/real-mixins-with-javascript-classes/#bettermixinsthroughclassexpressions) for example. – Patrick Roberts Feb 04 '21 at 20:46
  • this article is amazing, but it don't covers one think, I need a class factory that construct a proxy object. – Daniel de Andrade Varela Feb 04 '21 at 21:30
  • 1
    @DanieldeAndradeVarela the problem with a class factory that constructs a proxy object is that the constructor of the class being passed in will not have access to the proxy object. That is because the user's class will be constructed before the proxy class is. In your use-case, your users are the ones that need to define the class factories. – Patrick Roberts Feb 04 '21 at 21:33
  • you described exactly what my drama is! – Daniel de Andrade Varela Feb 04 '21 at 23:03