如何用JavaScript类语法定义可继承的实例字段?
类语法中定义可继承实例属性的简洁方案
用传统原型方式虽然繁琐,但能定义带有可继承属性的原型:
const SCXMLState = Object.setPrototypeOf( Object.defineProperties({ addChild() { }, isState: true // 可继承的属性和值 }, { isSCXML: { get() { return this.nodeName === 'scxml' } }, id: { get() { return this.getAttribute('id') || (this.isSCXML ? '(scxml)' : null); } } }), Element.prototype ); const markup = `<scxml xmlns="http://www.w3.org/2005/07/scxml" />`; const xmldoc = (new DOMParser).parseFromString(markup, "text/xml"); const scxml = xmldoc.documentElement; Object.setPrototypeOf(scxml, SCXMLState); console.log({id: scxml.id, isState: scxml.isState}); // {id: '(scxml)', isState: true}
使用类继承看似更优雅,但像isState这类实例字段不会通过原型继承,而是直接设置在实例上。由于未用new创建实例,该字段失效:
class SCXMLState extends Element { addChild() { } isState = true; get isSCXML() { return this.nodeName === 'scxml'; } get id() { return this.getAttribute('id') || (this.isSCXML ? '(scxml)' : null); } } const markup = `<scxml xmlns="http://www.w3.org/2005/07/scxml" />`; const xmldoc = (new DOMParser).parseFromString(markup, "text/xml"); const scxml = xmldoc.documentElement; Object.setPrototypeOf(scxml, SCXMLState.prototype); console.log({id: scxml.id, isState: scxml.isState}); // {id: '(scxml)', isState: undefined}
此时其他代码正常,但isState未被继承,显示undefined而非true。除了直接赋值SCXMLState.prototype.isState = true外,有没有更简洁、封装性更好的语法来定义类的可继承属性?
可行方案
1. 类内部使用静态块封装原型属性定义
ES2022引入的静态代码块允许在类定义内部直接修改原型,把属性定义封装在类的范围内,避免外部操作原型:
class SCXMLState extends Element { addChild() { } // 静态块内完成原型属性定义,保持封装性 static { this.prototype.isState = true; } get isSCXML() { return this.nodeName === 'scxml'; } get id() { return this.getAttribute('id') || (this.isSCXML ? '(scxml)' : null); } } // 测试代码 const markup = `<scxml xmlns="http://www.w3.org/2005/07/scxml" />`; const xmldoc = (new DOMParser).parseFromString(markup, "text/xml"); const scxml = xmldoc.documentElement; Object.setPrototypeOf(scxml, SCXMLState.prototype); console.log({id: scxml.id, isState: scxml.isState}); // {id: '(scxml)', isState: true}
2. 用getter模拟可继承的固定属性
如果属性值是固定不变的,可以用无参数的getter替代实例字段,getter本身会挂载在原型上,天然支持继承:
class SCXMLState extends Element { addChild() { } // 用getter定义可继承属性,简洁且无需额外语法 get isState() { return true; } get isSCXML() { return this.nodeName === 'scxml'; } get id() { return this.getAttribute('id') || (this.isSCXML ? '(scxml)' : null); } } // 测试代码 const markup = `<scxml xmlns="http://www.w3.org/2005/07/scxml" />`; const xmldoc = (new DOMParser).parseFromString(markup, "text/xml"); const scxml = xmldoc.documentElement; Object.setPrototypeOf(scxml, SCXMLState.prototype); console.log({id: scxml.id, isState: scxml.isState}); // {id: '(scxml)', isState: true}
内容的提问来源于stack exchange,提问作者Phrogz
相关产品推荐
相关产品推荐

