我想编写一些扩展 DOM 节点的 Javascript 类(以便我可以将我的类的实例直接插入到 DOM 中),但是很难找出我应该继承哪个类/原型。
例如:
function myExtendedElement() {
this.superclass = ClassA;
this.superclass();
delete this.superclass;
}
但是 ClassA 应该是什么?
我想编写一些扩展 DOM 节点的 Javascript 类(以便我可以将我的类的实例直接插入到 DOM 中),但是很难找出我应该继承哪个类/原型。
例如:
function myExtendedElement() {
this.superclass = ClassA;
this.superclass();
delete this.superclass;
}
但是 ClassA 应该是什么?
这样做不是一个好主意。
首先,要从 DOM 元素继承,您需要访问该元素的原型。问题是并非所有浏览器都提供对 DOM 元素原型的访问。例如,较新的 Gecko 和基于 WebKit 的客户端将其中一些原型公开为全局对象 - HTMLDivElement、HTMLElement、Element、Node 等。
例如,普通的 DIV 元素通常具有类似于以下内容的原型链:
HTMLDivElement.prototype -> HTMLElement.prototype -> Element.prototype
-> Node.prototype -> Object.prototype -> null
您可以访问它们中的任何一个,并根据需要扩展或继承。但同样,即使你可以,我强烈建议不要。
当浏览器不公开这些原型时,您就很不走运了。您可以尝试通过遵循constructor
DOM 元素本身的属性来检索它们-
document.createElement('div').constructor;
- 但是不能保证元素具有constructor
属性(例如 IE6 没有),即使它具有,该属性引用“正确”对象。毕竟,如果构造函数确实引用了正确的对象,则仍然不能保证完全允许扩充该对象。事实上,宿主对象可以实现完全奇怪的行为,甚至不必遵循原生 JS 对象遵循的规则(你可以在现实生活中找到几十个这样的例子)。
您想避免从 DOM 元素原型继承的第二个原因是这种继承机制并未真正在任何地方指定;它可能是古怪的、不可预测的,而且总体上是脆弱和不可靠的。
是的,您可以创建一个构造函数来初始化具有适当原型链的对象(即其中包含 DOM 原型):
function MyDivElement(){}
MyDivElement.prototype = HTMLDivElement.prototype;
var myDiv = new MyDivElement();
typeof myDiv.appendChild; // "function"
- 但这就是它的全部,并且整个方法的实用性受到原型中某些方法的限制,而没有其他 -
typeof myDivElement.nodeName; // "undefined"
myDivElement.innerHTML = '<span>foo<\/span>';
myDivElement.childNodes; // Error
在某些标准指定了从 DOM 原型继承的确切机制(并且浏览器实际上实现了该机制)之前,最好不要管它们,也许可以尝试其他方法- 例如包装器或装饰器模式而不是原型模式 :)
旧 Q 但现在有一个比“做”或“不做”更好的答案,因为 IE6 大部分已经不复存在了。首先,原型核心 ECMA 端点继承构造函数(如“数组”)是非常无害且有用的,如果您正确执行并进行测试以避免破坏现有方法。但是,在使用 Function 之前,一定要远离 Object 并认真思考。
但是,如果您在很多人/作者之间共享代码,或者处理 DOM 不确定性,通常最好使用新的工厂方法创建适配器/包装器对象以在继承方案中使用。
在这种情况下,我编写了 document.createExtEl 来创建包装的 DOM 元素,这些元素的可访问属性都可以通过原型获得。
使用以下内容,您的 div 的“超类”将是 HTMLExtDivElement(在这种情况下全局可用 - ew,但这只是一个示例)。对原始 HTMLElement 实例的可用属性的所有引用都位于包装器的原型中。注意:一些旧的 IE 属性不能作为引用传递,甚至不能在不抛出错误的情况下访问(很棒),这就是 try/catch 的用途。
您可以通过在循环包装实例可用属性之后添加逻辑来将丢失或标准化的属性放入其中来规范化公共属性,但我将把它留给您。
现在,为了 Pete 的爱,永远不要使用我的代码编写一些级联 16 次继承的愚蠢行为,然后在一些具有讽刺意味的流行库中实现我们都被迫处理,否则我会追捕你并大声引用“设计模式”,同时扔烂水果。
//Implementation just like document.createElement()
//document.createExtEl('div').tagName === 'DIV'
document.createExtEl = ( function(){ //returns a function below
var htmlTags = ['div','a'], //... add all the element tags you care to make extendable here
constructorMap = {},
i = htmlTags.length;
while(i--){
thisTag = htmlTags[i].toLowerCase();
constructorMap[ thisTag ] = function(){
var elObj = document.createElement(thisTag),
thisProto = this.constructor.prototype,
constructorName = 'HTMLExt' + thisTag.charAt(0).toUpperCase() + thisTag.slice(1) + 'Element';
alert(constructorName);
window[constructorName] = this.constructor; //adds a global reference you can access the new constructor from.
for(var x in elObj){ try{ thisProto[x] = elObj[x]; } catch(e){} }
}
}
//all of the above executes once and returned function accesses via closure
return function(tagName){
return new constructorMap[tagName.toLowerCase()]();
}
} )()
//Now in the case of a superclass/constructor for div, you could use HTMLExtDivElement globally
您可以简单地向 DOM 原型添加新函数,例如。
Element.prototype.myNameSpaceSomeFunction = function(...){...}
然后myNameSpaceSomeFunction
将存在于所有元素上。
2020 年,您可以通过扩展 HTML 元素轻松创建自定义元素。
class AppDrawer extends HTMLElement {...}
window.customElements.define('app-drawer', AppDrawer);
// Or use an anonymous class if you don't want a named constructor in current scope.
window.customElements.define('app-drawer', class extends HTMLElement {...});
更多信息和参考:自定义元素
我发现了一个可行的 hack ……至少,它使我能够通过 DOM 元素访问扩展对象属性,反之亦然。但这并不优雅。
var DOMelement = document.getElementById('myID'); // or $('#myID')[0]; in jQuery
DOMelement.extended = new extensionClass(this);
function extensionClass(element) {
this.element = element;
...
}