如何从DOM元素类继承

IT技术 javascript dom inheritance
2021-03-09 03:07:36

我想编写一些扩展 DOM 节点的 Javascript 类(以便我可以将我的类的实例直接插入到 DOM 中),但是很难找出我应该继承哪个类/原型。

例如:

function myExtendedElement() {
       this.superclass = ClassA;
       this.superclass();
       delete this.superclass;
} 

但是 ClassA 应该是什么?

5个回答

这样不是一个好主意

首先,要从 DOM 元素继承,您需要访问该元素的原型。问题是并非所有浏览器都提供对 DOM 元素原型的访问例如,较新的 Gecko 和基于 WebKit 的客户端将其中一些原型公开为全局对象 - HTMLDivElement、HTMLElement、Element、Node 等。

例如,普通的 DIV 元素通常具有类似于以下内容的原型链:

HTMLDivElement.prototype -> HTMLElement.prototype -> Element.prototype 
  -> Node.prototype -> Object.prototype -> null

您可以访问它们中的任何一个,并根据需要扩展或继承。但同样,即使你可以,我强烈建议不要

当浏览器不公开这些原型时,您就很不走运了您可以尝试通过遵循constructorDOM 元素本身的属性来检索它们-

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 原型继承的确切机制(并且浏览器实际上实现了该机制)之前,最好不要管它们,也许可以尝试其他方法- 例如包装器或装饰器模式而不是原型模式 :)

2021-05-06 03:07:36
> 5 年后,也许这已经过时了?
2021-05-07 03:07:36
这是过时的。您可以使用自定义元素扩展 DOM,这些元素需要从 HTMLElement 继承;至少自 2013 w3.org/TR/custom-elements以来,工作一直在进行中
2021-05-20 03:07:36
LOLz 的工作至少从 2013 年开始就一直在进行,但现在是 2017 年,并且registerElement在 caniuse.com上仍然非常红!
2021-05-21 03:07:36

旧 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将存在于所有元素上。

这不是我要找的东西,因为我想要几种不同类型的扩展元素,所以不能只过滤所有元素的原型
2021-04-23 03:07:36
请注意,这不适用于 IE8 之前的任何版本的 IE。
2021-05-13 03:07:36

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;
    ...
}
我对创建循环引用持谨慎态度,并将使用一些可笑的大实例来测试我的应用程序,看看是否有任何明显的影响。为什么增加宿主对象是不安全的?
2021-05-03 03:07:36
您实际上应该避免向这样的元素添加属性(也称为 expandos)。增加宿主对象不仅不安全,而且您可能会不小心在宿主对象和本机对象之间创建循环引用,这在 IE 的某些版本 (<8, IIRC) 中会泄漏
2021-05-19 03:07:36