document.registerElement - 为什么我们需要同时指定 'prototype' 和 'extends'?

document.registerElement - Why do we need to specify both 'prototype' and 'extends'?

假设我想扩展本机 button 元素,并创建我自己的 super-button 元素。据我所知,它必须遵循以下模式:

var SuperButton = document.registerElement('super-button', {
  prototype: Object.create(HTMLButtonElement.prototype),
  extends: 'button'
});

我觉得很奇怪 - prototypeextends 参数不是说同样的事情吗?如果我明确地说我的 super-button 使用 HTMLButtonElement 原型,为什么我还需要指定它扩展按钮元素?这不是多余的吗?对我来说,它看起来像是完全相同的信息。

来自Custom Elements specification

In general, the name of the element being extended cannot be determined simply by looking at what element interface it extends, as many elements share the same interface (such as q and blockquote both sharing HTMLQuoteElement).

换句话说,虽然它对于 <button> 个元素可能是冗余的,但它通常不是冗余的,规范需要支持一般情况。

不过,我认为它对 <button> 来说甚至都不是多余的,因为没有什么可以阻止你这样做:

var SuperButton = document.registerElement('super-button', {
  prototype: Object.create(HTMLButtonElement.prototype),
  extends: 'a'
});

为了澄清 Paulpro 的回答:extends 创建了 is= 语法。如果qblockquote都共享HTMLQuoteElement,而你扩展q,那么你可以写<q is="super-button">但不能写<blockquote is="super-button">.

关于用另一个元素的 prototype 扩展 a link 意味着什么,这将创建一个没有适当功能和属性的 link,这不好:

参见:https://jsfiddle.net/3g0pus8r/

<a is="anchor-one" href="google.com">1st link</a>
<a is="anchor-two" href="google.com">2nd link</a>

var proto1 = Object.create(HTMLAnchorElement.prototype, {
  createdCallback: {
    value: function() { 
    console.log("1st:");
    console.log(HTMLAnchorElement.prototype.isPrototypeOf(this));
    console.log(this.hostname);
    console.log(this.toString());
    }
  }
});

var proto2 = Object.create(HTMLButtonElement.prototype, {
  createdCallback: {
    value: function() {        
    console.log("\n2nd:");
    console.log(HTMLAnchorElement.prototype.isPrototypeOf(this));
    console.log(this.hostname);
    console.log(this.toString());
    }
  }
});

document.registerElement("anchor-one",{prototype:proto1, extends: "a"});
document.registerElement("anchor-two",{prototype:proto2, extends: "a"});

结果是:

1st:
true
fiddle.jshell.net
https://fiddle.jshell.net/_display/google.com

2st:
false
undefined
[object HTMLButtonElement]

但是,您可能还想:

  • 扩展 MyOtherAnchor 进而扩展 HTMLAnchorElement
  • 扩展HTMLElement并自己实现所有锚点接口。
  • 使用不同的非标准界面创建锚点。

实际上,它允许您区分 Custom Tags declaration versus Type Extensions 声明(正如 Bergi 的第二条评论所建议的),因为它们共享相同的方法 document.registerElement().

类型扩展:使用extends

document.registerElement( "button-v2",  { 
    prototype: Object.create( HTMLButtonElement.prototype ),
    extends: 'button'
} )

<button-v2> 将继续充当 <button>ie 保持其语义)。

自定义标签:不要使用 extends

document.registerElement( "not-button",  { 
    prototype: Object.create( HTMLButtonElement.prototype )
} )

<not-button> 继承自 HTMLButtonElement 接口但失去了语义(ie 不会充当 <button>


注意:如果唯一的原因真的是因为你不能总是从它的原型链中推断出一个 <element>,那么就会提出一个可选参数来消除这种罕见情况的歧义。在这里,他们选择了一种常见的 synthetic 方法,乍一看会令人困惑,你是对的!