我想知道為什么new HTMLElement()和其他人(例如new HTMLDivElement())拋出非法建構式錯誤。
我想要這樣的代碼:
// create a square with a given length
class Square extends HTMLDivElement {
constructor(len) {
super();
this.style.width = `${len}px`;
this.style.height = `${len}px`;
this.style.border = '1px solid black';
}
}
const square = new Square(5);
document.body.appendChild(square);
但是,上面的代碼不亞于只是一個愿望,因為上面的代碼拋出了非法建構式錯誤。
我認為有兩種解決方法:(1)使用 $ref (2)使用自定義元素。請讓我展示一下。
(1) 使用 $ref
class Square {
constructor(len) {
const $div = document.createElement('div');
$div.style.width = `${len}px`;
$div.style.height = `${len}px`;
$div.style.border = '1px solid black';
this.$ref = $div;
}
}
const square = new Square(5); // returns Square object
document.body.appendChild(square.$ref);
這樣,我可以制作一個正方形,但不方便,因為我無法直接訪問 div(=$ref)。我需要先通過“方形”物件。(我想說的是和之間的區別appendChild(square))appendChild(square.$ref)因為new HTMLDivElement()不可能,我別無選擇,只能使用一個像包裝器一樣的物件。
(2) 使用(自治)自定義元素——也使用 $ref 。
class Square extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
const $div = document.createElement('div');
this.shadowRoot.appendChild($div);
this.shadowRoot.$ref = $div;
}
connectedCallback() {
this.shadowRoot.$ref.style.width = `${this.dataset.len}px`;
this.shadowRoot.$ref.style.height = `${this.dataset.len}px`;
this.shadowRoot.$ref.style.border = '1px solid black';
}
}
customElements.define('custom-square', Square);
const square = document.createElement('custom-square');
square.setAttribute('data-len', 5);
document.body.appendChild(square);
In this case, new Square(5) cannot be done because of the illegal constructor error, so I must use data-len custom attribute and connectedCallback() in order to apply data-len to $ref.style when square is appended to DOM.
Of course I can use both ways, but still I wonder why new HTMLElement() is prohibited. Are there any historical reasons for that?
Also, what makes me wonder is that even though new HTMLElement() throws illegal constructor error, how document.createElement() can make a new instance of HTMLElement (internally)? According to the first answer of this question, HTMLElement.constructor() throws an exception, not creating an object. Then how HTMLDivElement is instantiated and returned to me when I call document.createElement('div')?
In addition, I saw an answer that uses Object.create() method to create an instance of HTMLElement. It works and returns HTMLElement instance, but seems the HTMLElement instance doesn't work correctly. For example,
const test = Object.create(HTMLDivElement.prototype, {});
test.textContent = 'test'; // illegal invocation
For some reason I can get illegal invocation error. Why is that?
uj5u.com熱心網友回復:
最簡單的答案是HTMLDivElement和HTMLElement是介面,并非所有介面都是可構造的。
這些介面不可構造的原因可以通過查看HTMLElementMDN上的繼承鏈找到:
EventTarget ← 節點 ← 元素 ← HTMLElement
由于這些是 DOM 介面,我們可以在DOM Living Standard中找到有關它們的更多資訊。具體來看Node,DOM 標準有以下注釋:
Node 是所有節點都使用的抽象介面。您無法獲得它的直接實體。
所以 Node 是明確不可構造的。
每個節點都有一個關聯的節點檔案,在創建時設定,即檔案。
這就解釋了為什么我們需要使用document.createElement()來創建一個 HTML 元素;節點和從它繼承的物件必須與檔案相關聯。
可以將Node、Element和HTMLElement建構式更改為接受document引數嗎?嗯,是的,但那會比 更好document.createElement()嗎?
值得注意的是,在繼承鏈中更進一步,EventTarget最近被更改為可構造的,因為它沒有與Node.
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/422076.html
標籤:
