Search code examples
javascriptweb-component

Javascript create custom attribute getter (like in python)


Say I have a class in JavaScript (yes bad bad class, bad class in JS, but its for web components, one has to use classes).

I would like to create a cached attribute getter for elements on a class, in python it would be this:

class Foo(object):
    _elements = {}
    def __getattr__(self, name):
        if name in ['widget1', 'widget2', 'widget3']:  # A long list of items, don't want to create getters for each one individiually
            if not _elements.get(name):
                self._elements[name] = self.getElementById(name)     
            return self._elements[name] 
       else:
            # Default behaviour
            return object.__getattr__(self, name)

This is the closest I got, but its ugly to use:

  • One must call it as this.el['widget1']
  • Instead of this.widget1
class Foo extends HTMLElement {
    el(id) {
        // Cached get element by id
        this._els = this._els || {};
        if (!this._els[id]) {
            this._els[id] = this.getElementById(id)
        }
        return this._els[id]
    }

Solution

  • getElementById is slower

    But does your cached performance gain outway extra code, code complexity and time coding?

    1 PICO second equals 0.00000001 Milliseconds

    <div style="display:grid;grid-template-columns:1fr 1fr">
      <test-component id="CACHED"></test-component>
      <test-component id="BYID"></test-component>
    </div>
    <div id="DIFF"><b>100.000 calls per run:</b></div>
    <script>
      customElements.define("test-component", class extends HTMLElement {
        el(id) {
          this._els = this._els || {};
          if (!this._els[id]) this._els[id] = document.getElementById(id);
          return this._els[id]
        }
        get CACHED() {
          return this.el("CACHED");
        }
        get BYID() {
          return document.getElementById("BYID");
        }
        connectedCallback() {
          let count = 100000;
          for (let run = 1; run < 9; run++) {
            let log = [];
            for (let cnt = 0; cnt < count; cnt++) {
              const t0 = performance.now();
              this == CACHED ? this.CACHED : this.BYID;// test performance
              log.push(performance.now() - t0);
            }
            this.average = (log.reduce((a, b) => a + b) / log.length)*1e9;
            let diff = (BYID.average - CACHED.average).toPrecision(2);
            if (this == BYID) DIFF.innerHTML += `<div>Run #${run} = <b>${diff/count}</b> PICO seconds slower per call<div>`;
          }
        }
      })
    </script>