Digamos que tengo una clase en JavaScript (sí, mala mala clase, mala clase en JS, pero es para componentes web, uno tiene que usar clases).
Me gustaría crear un captador de atributos en caché para los elementos de una clase, en Python sería este:
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)
Esto es lo más cercano que tengo, pero es feo de usar:
this.el['widget1']
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] }
getElementById
es más lento
Pero, ¿su rendimiento almacenado en caché gana código adicional, complejidad de código y codificación de tiempo?
1 segundo PICO equivale a 0,00000001 milisegundos
<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>
Para cualquier otra persona que tenga el mismo problema, esto es lo que hice, bastante código por adelantado, pero al menos es fácil de usar, por ejemplo, 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') }