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)
这是我得到的最接近的,但使用起来很丑陋:
- 必须这样称呼它
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]
}
对于遇到同样问题的任何其他人,这就是我所采用的方法,前期代码很多,但至少它易于使用,例如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>
假设我在 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)
这是我得到的最接近的,但使用起来很丑陋:
- 必须这样称呼它
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]
}
对于遇到同样问题的任何其他人,这就是我所采用的方法,前期代码很多,但至少它易于使用,例如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>