Javascript 创建自定义属性 getter(如 python)

Javascript create custom attribute getter (like in python)

假设我在 JavaScript 中有一个 class(是的,糟糕的 class,JS 中的糟糕的 class,但它对于 Web 组件,必须使用 classes).

我想为 class 上的元素创建缓存属性 getter,在 python 中是这样的:

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)

这是我得到的最接近的,但使用起来很丑陋:

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]
    }

对于遇到同样问题的任何其他人,这就是我所采用的方法,前期代码很多,但至少它易于使用,例如this.PROGRESS

class Foo extends HTMLElement {
    el(id, docEl=false) {
        // Cached get element by name
        // Parameter: docEl
        //  Set to true to search the document instead of the shadowRoot
        this._els = this._els || {};
        if (!this._els[id]) {
            const searchNode = docEl ? document : this.shadowRoot;
            this._els[id] = searchNode.getElementById(id)
        }
        return this._els[id]
    }

    // Document Elements
    get TEMPLATE()          { return this.el('TEMPLATE_wc-uploader', true) }
    get TEMPLATE_WC_UPLOAD(){ return this.el('TEMPLATE_wc-upload', true) }
    // Shadow Root Elements
    get PROGRESS()          { return this.el('PROGRESS') }
    get PROGRESS_HEADING()  { return this.el('PROGRESS_HEADING') }
    get DRAG_OVERLAY()      { return this.el('DRAG_OVERLAY') }
    get UPLOAD_LIST()       { return this.el('UPLOAD_LIST') }

getElementById 较慢

但是您的缓存性能是否获得了额外的代码、代码复杂性和时间编码?

1 PICO 秒 等于 0.00000001 毫秒

<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>