Search code examples
javascriptfunctionclassmethodsgetter-setter

Setter in JavaScript class that can return


So I have a class XYZ in JavaScript, it has few functions:

class XYZ{
    constructor(){
        this.text=null;
    }
    f1(){
      // Do Something
      return this;
    }

    f2(){
      // Do Something
      return this;
    }

    f3(txt=null){
      if(txt){
          this.text=txt;
          this.show();
      }
      // Do Something
      return this;
    }

    show(){
      console.log(this.text)
    }
}

const obj = new XYZ();
obj.f1().f2().f3("Hello").f1();
obj.f1().f2().f3().f2();

What I want to achieve is that if there is no text to pass to f3, I want to skip the brackets and use it like a getter:

const obj = new XYZ();
obj.f1().f2().f3("Hello").f1();
obj.f1().f2().f3.f2();

I can achieve something similar like this using getter and setter, but I don't want to break the chain calling. Is there a way where we can skip braces if not passing a value, or using the same name getter and method together? Thanks


Solution

  • You can use Proxy to redirect getters invoked on methods back to the instance with another Proxy. If a method's getter is invoked (like f1.f2) - call the method (f1):

    class XYZ {
        constructor() {
            this.text = null;
            const handler = {
                get(_, prop) {
                    if (typeof instance[prop] === 'function') {
                        const out = new Proxy(function(...args) {
                            return instance[prop].call(proxy, ...args);
                        }
                        , {
                          get(){
                            // the next chained method is requested, so call the current method
                            out();
                            return handler.get(...arguments);
                          }
                        });
                       return out; 
                       
                    }
                    return instance[prop];
                }
            };
            const instance = this;
            const proxy = new Proxy(this,handler);
            return proxy;
        }
        f1() {
            this.log('f1');
            return this;
        }
    
        f2() {
            this.log('f2');
            return this;
        }
    
        f3(txt=null) {
            if (txt) {
                this.text = txt;
                this.show();
            }
            this.log('f3');
            return this;
        }
    
        show() {
            console.log(this.text)
        }
        log(name){
          console.log('calling', name + '()', 'on', JSON.stringify(this));
        }
    }
    
    const obj = new XYZ();
    
    obj.f1.f2.f3("Hello").f1.show();
    
    const obj2 = new XYZ();
    obj2.f1.f2.f3.f2.show();
    .as-console-wrapper { top: 0; max-height: 100% !important; }