从(扩展)HTML元素中进行子类化?

时间:2009-09-06 00:12:21

标签: javascript html dom

如果我可以创建自己的扩展HTML元素类的类,我会喜欢它,例如HTMLDivElement或HTMLImageElement。

假设标准继承模式:

// class A:

function ClassA(foo) {
    this.foo = foo;
}

ClassA.prototype.toString = function() {
    return "My foo is " + this.foo;
};

// class B:

function ClassB(foo, bar) {
    ClassA.call(this, foo);
    this.bar = bar;
}

ClassB.prototype = new ClassA();

ClassB.prototype.toString = function() {
    return "My foo/bar is " + this.foo + "/" + this.bar;
};

我无法弄清楚构造函数中要调用的内容:

function Image2() {
    // this doesn't work:
    Image2.prototype.constructor.call(this);
    // neither does this (only applies to <img>, no equivalent for <span> etc.):
    Image.call(this);
}

Image2.prototype = new Image(); // or document.createElement("img");

Image2.prototype.toString = function() {
    return "My src is " + this.src;
};

这是不可能的吗?还是有某种方法?谢谢。 =)

5 个答案:

答案 0 :(得分:4)

好的,这个问题需要更新当前的技术。

HTML5提供了创建自定义元素的方法。您需要通过document.registerElement注册您的元素,之后您可以像使用任何其他标签一样使用它。该功能已在最新的Chrome中提供。我不确定其他浏览器。

此处有更多详情:

http://www.html5rocks.com/en/tutorials/webcomponents/customelements/

答案 1 :(得分:3)

  

我无法弄清楚构造函数中要调用的内容

     

Image2.prototype.constructor.call(本);

是的,JavaScript的对象没有像这样的类和可链接的构造函数。它有自己的原型模型,这可能很混乱,但实际上并没有提供原型继承的适当潜力,因为它仍然依赖于构造函数,它看起来像类构造函数但不是真的。

  

遗憾的是,这并不是真正创建子类;它只是伪装扩展实例

是的 - 这就是所有JavaScript给你的。如果你愿意,你当然可以创建自己的对象系统来抽象出来,所以看起来你正在创建类和实例。我附上了一个经常在底部使用的味道作为例子。但是在JavaScript中创建任何子类都没有标准。

例如,在您的“标准”代码中,您说:

  

ClassA.call(this,foo);

但这只能起作用,因为你已经用这样的方式编写了ClassA,你可以通过调用它两次,一次用于原型ClassA,将'foo'设置为'undefined',然后再继续调用继承的对象从那个原型中,掩盖了另一个'foo'背后的'foo'。

对于彼此而言,这并不是任何实际标准的类,并且当然没有指定这将适用于非JS本机浏览器对象:

function MyImage() {
    Image.call(this); // might do anything. will almost certainly fail
}
MyImage.prototype= new Image();

在任何情况下,ECMAScript标准都没有指定原型设置为非本机JavaScript对象的HTMLElement,并且具有不同程度的成功;在IE中它根本不起作用,原型的属性不需要。

此外,

  

Image2.prototype.constructor

您的示例中的

实际上可能与您期望的“图像”不同。 “构造函数”除了是一个在所有浏览器中都不可用的非标准属性之外,还会在类系统中做出一些根本不是你想到的东西;最好避免。

  

Image2应具有Image plus的所有属性/方法

目前,唯一的方法是使用某种包装器。由于上述原因,您无法使用原型执行此操作;你必须分别为每个实例添加属性:

function makeImage2() {
    var image= Image();
    image.isBig= function() {
        return this.width>200 || this.height>200;
    };
}

这当然只是一个工厂功能,而不是像子类化那样的东西。您必须为instanceof提供自己的替代品。

...

// An example class system
//
Function.prototype.subclass= function() {
    var c= new Function(
        'if (!(this instanceof arguments.callee)) throw(\'Constructor called without "new"\'); '+
        'if (arguments[0]!==Function.prototype.subclass._FLAG && this._init) this._init.apply(this, arguments); '
    );
    if (this!==Object)
        c.prototype= new this(Function.prototype.subclass._FLAG);
    return c;
};
Function.prototype.subclass._FLAG= {};

// Usage
//
ClassA= Object.subclass();
ClassA.prototype._init= function(foo) {
    this.foo= foo;
};
ClassA.prototype.toString= function() {
    return 'My foo is '+this.foo;
};

ClassB= ClassA.subclass();
ClassB.prototype._init= function(foo, bar) {
    ClassA.prototype._init.call(this, foo);
    this.bar= bar;
};
ClassB.prototype.toString= function() {
    return ClassA.prototype.toString.call(this)+' and my bar is '+this.bar;
};

答案 2 :(得分:2)

这可能会对你有所帮助。在这个基于Javascript的游戏中,他扩展了DOM中的table个元素。 MiniRacer on CodeProject

我相信他从this article by Paul Sowden得到了他的技巧。

答案 3 :(得分:1)

不,你不能。 (至少在今天的浏览器和IE6之间不可靠;我不知道最近的浏览器情况是否最近发生了变化。)

答案 4 :(得分:1)

David发布的链接提供了一个涉及工厂方法的解决方案。它不是任何方式的继承,但它提供了一种方便的方法来生成用您想要的属性和方法修饰的DOM元素的实例。您可以将装饰器调用链接到“构建”专用实例。

工厂方法(您不必使用Object.defineProperty):

// Create a custom div with a setter and a method.
function newElement(){
    // Create an element to decorate.
    var el = document.createElement('div');
    // A "private" variable.
    var _myVar = null;
    // Create a setter
    Object.defineProperty(el, "myVar", {
        set : function(value){
            _myVar = value;
            this.innerHTML = value;
        }
    });
    // Create a method
    Object.defineProperty(el, "saySomething", {
        value : function(){
            alert(_myVar);
        }
    });
    return el;
}

使用中:

var el = newElement();
el.myVar = "Boo!";
el.saySomething(); // alerts "Boo!"
document.body.appendChild(el); // A div with text "Boo!" is added to the body