Skip to content

ProxyClass

Wyatt Greenway edited this page Dec 13, 2022 · 3 revisions

class ProxyClass 📜

This is essentially a Proxy converted into class form. What that means is that instead of defining a proxy by passing it a "handlers" object to it, this instead is the handler for all classes that inherit from it. Just like a Proxy, inheriting from this class will allow the child-class to intercept property gets and sets, intercept method calls, property deletion, etc...

It works by returning this inside the constructor wrapped in a Proxy. The Proxy it creates is then managed by the class instance itself. For example, during key access, if a key the user is requesting is not found, the proxy will call the instance method MISSING on the class. This allows the child class to provide a method for MISSING, and then respond to key access for keys that don't actually exist on the instance.

That is just one example of many. This class provides full Proxy support, and so has methods (or stubs) for every feature available natively to a Proxy. Instance methods are keyed by symbols. This is to try and reduce the chance of a name collision... keeping this class useful for many scenarios. For example, the MISSING method above is actually Symbol.for('@_mythix/orm/ProxyClass/missing'), that is assigned to the constant ProxyClass.MISSING.

static property ProxyClass::APPLY = APPLY 📜


static property ProxyClass::AUTO_CALL = AUTO_CALL 📜


static property ProxyClass::AUTO_CALL_CALLED = AUTO_CALL_CALLED 📜


static property ProxyClass::AUTO_CALL_CALLER = AUTO_CALL_CALLER 📜


static property ProxyClass::CALLABLE = CALLABLE 📜


static property ProxyClass::CONSTRUCT = CONSTRUCT 📜


static property ProxyClass::DEFINE_PROPERTY = DEFINE_PROPERTY 📜


static property ProxyClass::DELETE_PROPERTY = DELETE_PROPERTY 📜


static property ProxyClass::GET = GET 📜


static property ProxyClass::GET_OWN_PROPERTY_DESCRIPTOR = GET_OWN_PROPERTY_DESCRIPTOR 📜


static property ProxyClass::GET_PROTOTYPEOF = GET_PROTOTYPEOF 📜


static property ProxyClass::HAS = HAS 📜


static property ProxyClass::IS_EXTENSIBLE = IS_EXTENSIBLE 📜


static property ProxyClass::MISSING = MISSING 📜


static property ProxyClass::OWN_KEYS = OWN_KEYS 📜


static property ProxyClass::PREVENT_EXTENSIONS = PREVENT_EXTENSIONS 📜


static property ProxyClass::PROXY = PROXY 📜


static property ProxyClass::SELF = SELF 📜


static property ProxyClass::SET = SET 📜


static property ProxyClass::SET_PROTOTYPEOF = SET_PROTOTYPEOF 📜


static property ProxyClass::shouldSkipProxy = shouldSkipProxy 📜


static property ProxyClass::TARGET = TARGET 📜


method ProxyClass::___autoCall(
    caller: Function,
): Function
📜

Any method of the instance wrapped in an __autoCall factory will be automatically called by the engine if not called by the user.

This works by the ProxyClass pushing the auto-call into a queue when the method key is accessed. If another key is accessed (any other key), then the ProxyClass will check if the auto-call method has been called yet. If it hasn't, then the ProxyClass will call it, providing no arguments, and using the return value of the call for the pending key access. If the auto-call method is simply called, then the queue is cleared, and the return value simply returned to the user.

Example:

  • class Greeter extends ProxyClass {
      greet = this.__autoCall((name) => {
        if (arguments.length === 0) {
          // An auto-call, or the user didn't
          // provide any arguments.
          console.log('Hello whoever you are!');
        } else {
          // Was definitely called by the user
          console.log(`Hello ${name}!`);
        }
      });
    
      finish() {
        // finish operation
      }
    }
    
    // Example 1
    let greeter = new Greeter();
    greeter.greet.finish();
    //           ^---- Auto call happens here
    // output: Hello whoever you are!
    
    // Example 2
    greeter.greet('Wyatt Greenway').finish();
    // No auto-call happens... this is a manual call.
    // output: Hello Wyatt Greenway!

Notes:

  • For an auto-call to work, a key access attempt must happen after the auto-call method is accessed. This is almost always the case, because in interacting with the object you are almost guaranteed to access a key again, i.e. .toString if converting to a string, .toJSON if converting to JSON, iterator access, or even debugging the object.

Arguments:

  • caller: Function

    The method implementation for the class. This method will be used by the factory to create an auto-call method for the class.

Return value: Function

The caller method provided, wrapped into an auto-call factory method.


method ProxyClass::___call(
    caller: Function,
): Function
📜

This is a factory much like ProxyClass.__autoCall for creating instance methods. It differs however in that the method returned by this factory isn't auto-called, but instead an optional call.

The way it works is that the method provided is returned, itself wrapped in a Proxy. If it is called, then the Proxy will pass the call through to the method, and return the result. Being a Proxy, it passes all key access back to the original class instance, allowing the method itself to mimic the class instance. This allows for instance methods that can optionally be called, but if they aren't called, will act as though you are still interacting with the instance of the class itself.

Example:

  • class Greeter extends ProxyClass {
      constructor() {
        super();
    
        this.greetName = undefined;
      }
    
      name = this.__call((name) => {
        this.greetName = name;
      });
    
      greet() {
        if (this.greetName) {
          console.log(`Hello ${this.greetName}!`);
        } else {
          console.log('Hello whoever you are!');
        }
      }
    }
    
    // Example 1
    let greeter = new Greeter();
    greeter.name.greet();
    //          ^---- optional call here
    // output: Hello whoever you are!
    
    // Example 2
    greeter.name('Wyatt Greenway').greet();
    // output: Hello Wyatt Greenway!

Arguments:

  • caller: Function

    The method implementation for the class. This method will be used by the factory to create an optional call method for the class.

Return value: Function

The caller method provided, wrapped into an optional call factory method.


method ProxyClass::constructor(): ProxyClass 📜

Construct the class instance, with this returned wrapped in a Proxy.

Return value: ProxyClass



Clone this wiki locally