JavaScript如何实现类似Python的自定义属性getter完成元素缓存获取
实现带缓存的Web Components元素属性访问方案
你可以通过Proxy属性拦截实现和Python __getattr__完全一致的效果,无需提前定义每个元素的getter,直接通过this.属性名访问对应id的元素。
方案1:Proxy拦截(推荐)
Proxy可以拦截所有实例属性访问请求,完全匹配你需要的动态匹配逻辑:
class Foo extends HTMLElement { constructor() { super(); // 初始化缓存对象 this._els = {}; // 返回代理后的实例,拦截所有属性访问 return new Proxy(this, { get(target, propName) { // 优先返回类本身已有的属性、方法,以及HTMLElement内置属性,避免覆盖原生能力 if (Reflect.has(target, propName) || propName in HTMLElement.prototype) { return Reflect.get(target, propName); } // 匹配到未知属性时,走缓存获取元素逻辑 if (!target._els[propName]) { // 如果你使用了Shadow DOM,这里需要改成 target.shadowRoot.getElementById(propName) target._els[propName] = target.getElementById(propName); } return target._els[propName]; } }); } } // 注册自定义元素 customElements.define('foo-component', Foo);
使用方式完全符合预期:
// 直接通过属性名获取对应id的元素,自带缓存 const widget = this.widget1;
方案2:批量预定义getter(兼容场景)
如果你需要兼容不支持Proxy的极端场景,可以提前维护所有需要用到的元素id列表,批量挂载getter:
// 提前维护所有需要访问的元素id const ELEMENT_ID_LIST = ['widget1', 'widget2', 'widget3', /* 其他id */]; class Foo extends HTMLElement { constructor() { super(); this._els = {}; // 批量为每个id定义getter ELEMENT_ID_LIST.forEach(id => { Object.defineProperty(this, id, { get() { if (!this._els[id]) { this._els[id] = this.getElementById(id); } return this._els[id]; }, configurable: true, enumerable: true }); }); } }
该方案的优势是没有代理开销,兼容性更好,缺点是需要提前知晓所有需要访问的元素id,无法动态适配新增的id。
注意事项
如果你在自定义元素中使用了Shadow DOM,需要将获取元素的逻辑从this.getElementById修改为this.shadowRoot.getElementById,否则无法获取到Shadow树内部的元素。
内容的提问来源于stack exchange,提问作者run_the_race
相关产品推荐
相关产品推荐

