讓我們更深入地了解一下 DOM 節(jié)點(diǎn)。
在本章中,我們將更深入地了解它們是什么,并學(xué)習(xí)它們最常用的屬性。
不同的 DOM 節(jié)點(diǎn)可能有不同的屬性。例如,標(biāo)簽 <a>
相對(duì)應(yīng)的元素節(jié)點(diǎn)具有鏈接相關(guān)的(link-related)屬性,標(biāo)簽 <input>
相對(duì)應(yīng)的元素節(jié)點(diǎn)具有與輸入相關(guān)的屬性,等。文本節(jié)點(diǎn)與元素節(jié)點(diǎn)不同。但是所有這些標(biāo)簽對(duì)應(yīng)的 DOM 節(jié)點(diǎn)之間也存在共有的屬性和方法,因?yàn)樗蓄?lèi)型的 DOM 節(jié)點(diǎn)都形成了一個(gè)單一層次的結(jié)構(gòu)(single hierarchy)。
每個(gè) DOM 節(jié)點(diǎn)都屬于相應(yīng)的內(nèi)建類(lèi)。
層次結(jié)構(gòu)(hierarchy)的根節(jié)點(diǎn)是 EventTarget,Node 繼承自它,其他 DOM 節(jié)點(diǎn)繼承自 Node。
下圖做了進(jìn)一步說(shuō)明:
類(lèi)如下所示:
該類(lèi)的對(duì)象從未被創(chuàng)建。它作為一個(gè)基礎(chǔ),以便讓所有 DOM 節(jié)點(diǎn)都支持所謂的“事件(event)”,我們會(huì)在之后學(xué)習(xí)它。
它提供了樹(shù)的核心功能:parentNode
,nextSibling
,childNodes
等(它們都是 getter)。Node
類(lèi)的對(duì)象從未被創(chuàng)建。但是還有一些繼承自它的其他類(lèi)(因此繼承了 Node
的功能)。
HTMLDocument
繼承(盡管最新的規(guī)范沒(méi)有規(guī)定)—— 是一個(gè)整體的文檔。全局變量 document
就是屬于這個(gè)類(lèi)。它作為 DOM 的入口。
<p>Hello</p>
? 中的 ?Hello
?。它提供了元素級(jí)導(dǎo)航(navigation),如 nextElementSibling
,children
,以及搜索方法,如 getElementsByTagName
和 querySelector
。
瀏覽器不僅支持 HTML,還支持 XML 和 SVG。因此,Element
類(lèi)充當(dāng)?shù)氖歉唧w的類(lèi)的基礎(chǔ):SVGElement
,XMLElement
(我們?cè)谶@里不需要它)和 HTMLElement
。
它會(huì)被更具體的 HTML 元素繼承:
<input>
? 元素的類(lèi),<body>
? 元素的類(lèi),<a>
? 元素的類(lèi),還有很多其他標(biāo)簽具有自己的類(lèi),可能還具有特定的屬性和方法,而一些元素,如 <span>
、<section>
、<article>
等,沒(méi)有任何特定的屬性,所以它們是 HTMLElement
類(lèi)的實(shí)例。
因此,給定節(jié)點(diǎn)的全部屬性和方法都是繼承鏈的結(jié)果。
例如,我們考慮一下 <input>
元素的 DOM 對(duì)象。它屬于 HTMLInputElement 類(lèi)。
它獲取屬性和方法,并將其作為下列類(lèi)(按繼承順序列出)的疊加:
HTMLInputElement
? —— 該類(lèi)提供特定于輸入的屬性,HTMLElement
? —— 它提供了通用(common)的 HTML 元素方法(以及 getter 和 setter)Element
? —— 提供通用(generic)元素方法,Node
? —— 提供通用 DOM 節(jié)點(diǎn)屬性,EventTarget
? —— 為事件(包括事件本身)提供支持,Object
?,因?yàn)橄?nbsp;?hasOwnProperty
? 這樣的“普通對(duì)象”方法也是可用的。我們可以通過(guò)回調(diào)來(lái)查看 DOM 節(jié)點(diǎn)類(lèi)名,因?yàn)閷?duì)象通常都具有 constructor
屬性。它引用類(lèi)的 constructor,constructor.name
就是它的名稱(chēng):
alert( document.body.constructor.name ); // HTMLBodyElement
……或者我們可以對(duì)其使用 toString
方法:
alert( document.body ); // [object HTMLBodyElement]
我們還可以使用 instanceof
來(lái)檢查繼承:
alert( document.body instanceof HTMLBodyElement ); // true
alert( document.body instanceof HTMLElement ); // true
alert( document.body instanceof Element ); // true
alert( document.body instanceof Node ); // true
alert( document.body instanceof EventTarget ); // true
正如我們所看到的,DOM 節(jié)點(diǎn)是常規(guī)的 JavaScript 對(duì)象。它們使用基于原型的類(lèi)進(jìn)行繼承。
在瀏覽器中,使用 console.dir(elem)
輸出元素來(lái)查看也是非常容易的。在控制臺(tái)中,你可以看到 HTMLElement.prototype
和 Element.prototype
等。
?
console.dir(elem)
? 與 ?console.log(elem)
?大多數(shù)瀏覽器在其開(kāi)發(fā)者工具中都支持這兩個(gè)命令:
console.log
和console.dir
。它們將它們的參數(shù)輸出到控制臺(tái)中。對(duì)于 JavaScript 對(duì)象,這些命令通常做的是相同的事。
但對(duì)于 DOM 元素,它們是不同的:
- ?
console.log(elem)
? 顯示元素的 DOM 樹(shù)。- ?
console.dir(elem)
? 將元素顯示為 DOM 對(duì)象,非常適合探索其屬性。你可以在
document.body
上嘗試一下。
規(guī)范中的 IDL
在規(guī)范中,DOM 類(lèi)不是使用 JavaScript 來(lái)描述的,而是一種特殊的 接口描述語(yǔ)言(Interface description language),簡(jiǎn)寫(xiě)為 IDL,它通常很容易理解。
在 IDL 中,所有屬性以其類(lèi)型開(kāi)頭。例如,
DOMString
和boolean
等。
以下是摘錄(excerpt),并附有注釋?zhuān)?
// 定義 HTMLInputElement // 冒號(hào) ":" 表示 HTMLInputElement 繼承自 HTMLElement interface HTMLInputElement: HTMLElement { // 接下來(lái)是 <input> 元素的屬性和方法 // "DOMString" 表示屬性的值是字符串 attribute DOMString accept; attribute DOMString alt; attribute DOMString autocomplete; attribute DOMString value; // 布爾值屬性(true/false) attribute boolean autofocus; ... // 現(xiàn)在方法:"void" 表示方法沒(méi)有返回值 void select(); ... }
nodeType
屬性提供了另一種“過(guò)時(shí)的”用來(lái)獲取 DOM 節(jié)點(diǎn)類(lèi)型的方法。
它有一個(gè)數(shù)值型值(numeric value):
elem.nodeType == 1
?,elem.nodeType == 3
?,elem.nodeType == 9
?,例如:
<body>
<script>
let elem = document.body;
// 讓我們檢查一下:elem 中的節(jié)點(diǎn)類(lèi)型是什么?
alert(elem.nodeType); // 1 => element
// 它的第一個(gè)子節(jié)點(diǎn)的類(lèi)型是……
alert(elem.firstChild.nodeType); // 3 => text
// 對(duì)于 document 對(duì)象,類(lèi)型是 9
alert( document.nodeType ); // 9
</script>
</body>
在現(xiàn)代腳本中,我們可以使用 instanceof
和其他基于類(lèi)的檢查方法來(lái)查看節(jié)點(diǎn)類(lèi)型,但有時(shí) nodeType
可能更簡(jiǎn)單。我們只能讀取 nodeType
而不能修改它。
給定一個(gè) DOM 節(jié)點(diǎn),我們可以從 nodeName
或者 tagName
屬性中讀取它的標(biāo)簽名:
例如:
alert( document.body.nodeName ); // BODY
alert( document.body.tagName ); // BODY
tagName 和 nodeName 之間有什么不同嗎?
當(dāng)然,差異就體現(xiàn)在它們的名字上,但確實(shí)有些微妙。
tagName
? 屬性?xún)H適用于 ?Element
? 節(jié)點(diǎn)。nodeName
? 是為任意 ?Node
? 定義的:tagName
? 相同。換句話(huà)說(shuō),tagName
僅受元素節(jié)點(diǎn)支持(因?yàn)樗鹪从?nbsp;Element
類(lèi)),而 nodeName
則可以說(shuō)明其他節(jié)點(diǎn)類(lèi)型。
例如,我們比較一下 document
的 tagName
和 nodeName
,以及一個(gè)注釋節(jié)點(diǎn):
<body><!-- comment -->
<script>
// for comment
alert( document.body.firstChild.tagName ); // undefined(不是一個(gè)元素)
alert( document.body.firstChild.nodeName ); // #comment
// for document
alert( document.tagName ); // undefined(不是一個(gè)元素)
alert( document.nodeName ); // #document
</script>
</body>
如果我們只處理元素,那么 tagName
和 nodeName
這兩種方法,我們都可以使用,沒(méi)有區(qū)別。
標(biāo)簽名稱(chēng)始終是大寫(xiě)的,除非是在 XML 模式下
瀏覽器有兩種處理文檔(document)的模式:HTML 和 XML。通常,HTML 模式用于網(wǎng)頁(yè)。只有在瀏覽器接收到帶有
Content-Type: application/xml+xhtml
header 的 XML-document 時(shí),XML 模式才會(huì)被啟用。
在 HTML 模式下,
tagName/nodeName
始終是大寫(xiě)的:它是BODY
,而不是<body>
或<BoDy>
。
在 XML 模式中,大小寫(xiě)保持為“原樣”。如今,XML 模式很少被使用。
innerHTML 屬性允許將元素中的 HTML 獲取為字符串形式。
我們也可以修改它。因此,它是更改頁(yè)面最有效的方法之一。
下面這個(gè)示例顯示了 document.body
中的內(nèi)容,然后將其完全替換:
<body>
<p>A paragraph</p>
<div>A div</div>
<script>
alert( document.body.innerHTML ); // 讀取當(dāng)前內(nèi)容
document.body.innerHTML = 'The new BODY!'; // 替換它
</script>
</body>
我們可以嘗試插入無(wú)效的 HTML,瀏覽器會(huì)修復(fù)我們的錯(cuò)誤:
<body>
<script>
document.body.innerHTML = '<b>test'; // 忘記閉合標(biāo)簽
alert( document.body.innerHTML ); // <b>test</b>(被修復(fù)了)
</script>
</body>
腳本不會(huì)執(zhí)行
如果
innerHTML
將一個(gè)<script>
標(biāo)簽插入到 document 中 —— 它會(huì)成為 HTML 的一部分,但是不會(huì)執(zhí)行。
我們可以使用 elem.innerHTML+="more html"
將 HTML 附加到元素上。
就像這樣:
chatDiv.innerHTML += "<div>Hello<img src='smile.gif'/> !</div>";
chatDiv.innerHTML += "How goes?";
但我們必須非常謹(jǐn)慎地使用它,因?yàn)槲覀兯龅?nbsp;不是 附加內(nèi)容,而且完全地重寫(xiě)。
從技術(shù)上來(lái)說(shuō),下面這兩行代碼的作用相同:
elem.innerHTML += "...";
// 進(jìn)行寫(xiě)入的一種更簡(jiǎn)短的方式:
elem.innerHTML = elem.innerHTML + "..."
換句話(huà)說(shuō),innerHTML+=
做了以下工作:
innerHTML
?(新舊結(jié)合)。因?yàn)閮?nèi)容已“歸零”并從頭開(kāi)始重寫(xiě),因此所有的圖片和其他資源都將重寫(xiě)加載。
在上面的 chatDiv
示例中,chatDiv.innerHTML+="How goes?"
重建了 HTML 內(nèi)容并重新加載了 smile.gif
(希望它是緩存的)。如果 chatDiv
有許多其他文本和圖片,那么就很容易看到重新加載(譯注:是指在有很多內(nèi)容時(shí),重新加載會(huì)耗費(fèi)更多的時(shí)間,所以你就很容易看見(jiàn)頁(yè)面重載的過(guò)程)。
并且還會(huì)有其他副作用。例如,如果現(xiàn)有的文本被用鼠標(biāo)選中了,那么大多數(shù)瀏覽器都會(huì)在重寫(xiě) innerHTML
時(shí)刪除選定狀態(tài)。如果這里有一個(gè)帶有用戶(hù)輸入的文本的 <input>
,那么這個(gè)被輸入的文本將會(huì)被移除。諸如此類(lèi)。
幸運(yùn)的是,除了 innerHTML
,還有其他可以添加 HTML 的方法,我們很快就會(huì)學(xué)到。
outerHTML
屬性包含了元素的完整 HTML。就像 innerHTML
加上元素本身一樣。
下面是一個(gè)示例:
<div id="elem">Hello <b>World</b></div>
<script>
alert(elem.outerHTML); // <div id="elem">Hello <b>World</b></div>
</script>
注意:與 innerHTML
不同,寫(xiě)入 outerHTML
不會(huì)改變?cè)?。而是?DOM 中替換它。
是的,聽(tīng)起來(lái)很奇怪,它確實(shí)很奇怪,這就是為什么我們?cè)谶@里對(duì)此做了一個(gè)單獨(dú)的注釋??匆幌?。
考慮下面這個(gè)示例:
<div>Hello, world!</div>
<script>
let div = document.querySelector('div');
// 使用 <p>...</p> 替換 div.outerHTML
div.outerHTML = '<p>A new element</p>'; // (*)
// 蛤!'div' 還是原來(lái)那樣!
alert(div.outerHTML); // <div>Hello, world!</div> (**)
</script>
看起來(lái)真的很奇怪,對(duì)吧?
在 (*)
行,我們使用 <p>A new element</p>
替換 div
。在外部文檔(DOM)中我們可以看到的是新內(nèi)容而不是 <div>
。但是正如我們?cè)?nbsp;(**)
行所看到的,舊的 div
變量并沒(méi)有被改變。
outerHTML
賦值不會(huì)修改 DOM 元素(在這個(gè)例子中是被 ‘div’ 引用的對(duì)象),而是將其從 DOM 中刪除并在其位置插入新的 HTML。
所以,在 div.outerHTML=...
中發(fā)生的事情是:
div
? 被從文檔(document)中移除。<p>A new element</p>
? 被插入到其位置上。div
? 仍擁有其舊的值。新的 HTML 沒(méi)有被賦值給任何變量。在這兒很容易出錯(cuò):修改 div.outerHTML
然后繼續(xù)使用 div
,就好像它包含的是新內(nèi)容一樣。但事實(shí)并非如此。這樣的東西對(duì)于 innerHTML
是正確的,但是對(duì)于 outerHTML
卻不正確。
我們可以向 elem.outerHTML
寫(xiě)入內(nèi)容,但是要記住,它不會(huì)改變我們所寫(xiě)的元素(‘elem’)。而是將新的 HTML 放在其位置上。我們可以通過(guò)查詢(xún) DOM 來(lái)獲取對(duì)新元素的引用。
innerHTML
屬性?xún)H對(duì)元素節(jié)點(diǎn)有效。
其他節(jié)點(diǎn)類(lèi)型,例如文本節(jié)點(diǎn),具有它們的對(duì)應(yīng)項(xiàng):nodeValue
和 data
屬性。這兩者在實(shí)際使用中幾乎相同,只有細(xì)微規(guī)范上的差異。因此,我們將使用 data
,因?yàn)樗獭?
讀取文本節(jié)點(diǎn)和注釋節(jié)點(diǎn)的內(nèi)容的示例:
<body>
Hello
<!-- Comment -->
<script>
let text = document.body.firstChild;
alert(text.data); // Hello
let comment = text.nextSibling;
alert(comment.data); // Comment
</script>
</body>
對(duì)于文本節(jié)點(diǎn),我們可以想象讀取或修改它們的原因,但是注釋呢?
有時(shí),開(kāi)發(fā)者會(huì)將信息或模板說(shuō)明嵌入到 HTML 中的注釋中,如下所示:
<!-- if isAdmin -->
<div>Welcome, Admin!</div>
<!-- /if -->
……然后,JavaScript 可以從 data
屬性中讀取它,并處理嵌入的指令。
textContent
提供了對(duì)元素內(nèi)的 文本 的訪(fǎng)問(wèn)權(quán)限:僅文本,去掉所有 <tags>
。
例如:
<div id="news">
<h1>Headline!</h1>
<p>Martians attack people!</p>
</div>
<script>
// Headline! Martians attack people!
alert(news.textContent);
</script>
正如我們所看到,只返回文本,就像所有 <tags>
都被剪掉了一樣,但實(shí)際上其中的文本仍然存在。
在實(shí)際開(kāi)發(fā)中,用到這樣的文本讀取的場(chǎng)景非常少。
寫(xiě)入 textContent
要有用得多,因?yàn)樗试S以“安全方式”寫(xiě)入文本。
假設(shè)我們有一個(gè)用戶(hù)輸入的任意字符串,我們希望將其顯示出來(lái)。
innerHTML
?,我們將其“作為 HTML”插入,帶有所有 HTML 標(biāo)簽。textContent
?,我們將其“作為文本”插入,所有符號(hào)(symbol)均按字面意義處理。比較兩者:
<div id="elem1"></div>
<div id="elem2"></div>
<script>
let name = prompt("What's your name?", "<b>Winnie-the-Pooh!</b>");
elem1.innerHTML = name;
elem2.textContent = name;
</script>
<div>
? 獲取 name “作為 HTML”:所有標(biāo)簽都變成標(biāo)簽,所以我們可以看到粗體的 name。<div>
? 獲取 name “作為文本”,因此我們可以從字面上看到 ?<b>Winnie-the-Pooh!</b>
?。在大多數(shù)情況下,我們期望來(lái)自用戶(hù)的文本,并希望將其視為文本對(duì)待。我們不希望在我們的網(wǎng)站中出現(xiàn)意料不到的 HTML。對(duì) textContent
的賦值正好可以做到這一點(diǎn)。
“hidden” 特性(attribute)和 DOM 屬性(property)指定元素是否可見(jiàn)。
我們可以在 HTML 中使用它,或者使用 JavaScript 對(duì)其進(jìn)行賦值,如下所示:
<div>Both divs below are hidden</div>
<div hidden>With the attribute "hidden"</div>
<div id="elem">JavaScript assigned the property "hidden"</div>
<script>
elem.hidden = true;
</script>
從技術(shù)上來(lái)說(shuō),hidden
與 style="display:none"
做的是相同的事。但 hidden
寫(xiě)法更簡(jiǎn)潔。
這里有一個(gè) blinking 元素:
<div id="elem">A blinking element</div>
<script>
setInterval(() => elem.hidden = !elem.hidden, 1000);
</script>
DOM 元素還有其他屬性,特別是那些依賴(lài)于 class 的屬性:
value
? —— ?<input>
?,?<select>
? 和 ?<textarea>
?(?HTMLInputElement
?,?HTMLSelectElement
?……)的 value。href
? —— ?<a href="...">
?(?HTMLAnchorElement
?)的 href。id
? —— 所有元素(?HTMLElement
?)的 “id” 特性(attribute)的值。例如:
<input type="text" id="elem" value="value">
<script>
alert(elem.type); // "text"
alert(elem.id); // "elem"
alert(elem.value); // value
</script>
大多數(shù)標(biāo)準(zhǔn) HTML 特性(attribute)都具有相應(yīng)的 DOM 屬性,我們可以像這樣訪(fǎng)問(wèn)它。
如果我們想知道給定類(lèi)的受支持屬性的完整列表,我們可以在規(guī)范中找到它們。例如,在 https://html.spec.whatwg.org/#htmlinputelement 中記錄了 HTMLInputElement
。
或者,如果我們想要快速獲取它們,或者對(duì)具體的瀏覽器規(guī)范感興趣 — 我們總是可以使用 console.dir(elem)
輸出元素并讀取其屬性?;蛘咴跒g覽器的開(kāi)發(fā)者工具的元素(Elements)標(biāo)簽頁(yè)中探索“DOM 屬性”。
每個(gè) DOM 節(jié)點(diǎn)都屬于一個(gè)特定的類(lèi)。這些類(lèi)形成層次結(jié)構(gòu)(hierarchy)。完整的屬性和方法集是繼承的結(jié)果。
主要的 DOM 節(jié)點(diǎn)屬性有:
?nodeType
?
我們可以使用它來(lái)查看節(jié)點(diǎn)是文本節(jié)點(diǎn)還是元素節(jié)點(diǎn)。它具有一個(gè)數(shù)值型值(numeric value):?1
? 表示元素,?3
? 表示文本節(jié)點(diǎn),其他一些則代表其他節(jié)點(diǎn)類(lèi)型。只讀。
?nodeName/tagName
?
用于元素名,標(biāo)簽名(除了 XML 模式,都要大寫(xiě))。對(duì)于非元素節(jié)點(diǎn),?nodeName
? 描述了它是什么。只讀。
?innerHTML
?
元素的 HTML 內(nèi)容。可以被修改。
?outerHTML
?
元素的完整 HTML。對(duì) ?elem.outerHTML
? 的寫(xiě)入操作不會(huì)觸及 ?elem
? 本身。而是在外部上下文中將其替換為新的 HTML。
?nodeValue/data
?
非元素節(jié)點(diǎn)(文本、注釋?zhuān)┑膬?nèi)容。兩者幾乎一樣,我們通常使用 ?data
???梢员恍薷?。
?textContent
?
元素內(nèi)的文本:HTML 減去所有 ?<tags>
?。寫(xiě)入文本會(huì)將文本放入元素內(nèi),所有特殊字符和標(biāo)簽均被視為文本??梢园踩夭迦胗脩?hù)生成的文本,并防止不必要的 HTML 插入。
?hidden
?
當(dāng)被設(shè)置為 ?true
? 時(shí),執(zhí)行與 CSS ?display:none
? 相同的事。
DOM 節(jié)點(diǎn)還具有其他屬性,具體有哪些屬性則取決于它們的類(lèi)。例如,<input>
元素(HTMLInputElement
)支持 value
,type
,而 <a>
元素(HTMLAnchorElement
)則支持 href
等。大多數(shù)標(biāo)準(zhǔn)
HTML 特性(attribute)都具有相應(yīng)的 DOM 屬性。
然而,但是 HTML 特性(attribute)和 DOM 屬性(property)并不總是相同的,我們將在下一章中看到。
這里有一個(gè)樹(shù)結(jié)構(gòu)嵌套的 ul/li
。
編寫(xiě)代碼,為每個(gè) <li>
顯示:
<li>
? 的數(shù)量 —— 所有后代,包括深層嵌套的后代。我們使用循環(huán)遍歷 <li>
:
for (let li of document.querySelectorAll('li')) {
...
}
循環(huán)時(shí),我們需要獲取每個(gè) li
中的文本。
我們可以從 li
的第一個(gè)子節(jié)點(diǎn)讀取文本,即文本節(jié)點(diǎn):
for (let li of document.querySelectorAll('li')) {
let title = li.firstChild.data;
// title 是在 <li> 中的任何其他節(jié)點(diǎn)之前的文本
}
然后我們就可以使用 li.getElementsByTagName('li')
來(lái)獲取后代的數(shù)目了。
下面這個(gè)腳本會(huì)顯示什么?
<html>
<body>
<script>
alert(document.body.lastChild.nodeType);
</script>
</body>
</html>
這里有一個(gè)陷阱。
在 <script>
執(zhí)行時(shí),最后一個(gè) DOM 節(jié)點(diǎn)就是 <script>
,因?yàn)闉g覽器還沒(méi)有處理頁(yè)面的其余部分。
所以結(jié)果是 1
(元素節(jié)點(diǎn))。
<html>
<body>
<script>
alert(document.body.lastChild.nodeType);
</script>
</body>
</html>
這段代碼會(huì)顯示什么?
<script>
let body = document.body;
body.innerHTML = "<!--" + body.tagName + "-->";
alert( body.firstChild.data ); // 這里會(huì)顯示什么?
</script>
答案:BODY
。
<script>
let body = document.body;
body.innerHTML = "<!--" + body.tagName + "-->";
alert( body.firstChild.data ); // BODY
</script>
讓我們一步一步來(lái)看看發(fā)生了什么:
<body>
? 中的內(nèi)容被注釋所取代。注釋為 ?<!--BODY-->
?,因?yàn)?nbsp;?body.tagName == "BODY"
?。正如我們所記得的,在 HTML 模式下,?tagName
? 總是大寫(xiě)的。body.firstChild
? 中獲取了它。data
? 屬性是它的內(nèi)容(在 ?<!--...-->
? 內(nèi)的):?"BODY"
?。document
屬于哪一類(lèi)?
它位于 DOM 層次結(jié)構(gòu)(hierarchy)中的什么位置?
它繼承自 Node
還是 Element
,或者可能是 HTMLElement
?
我們可以通過(guò)輸出它,來(lái)看看它是屬于哪個(gè)類(lèi)的,像這樣:
alert(document); // [object HTMLDocument]
或者:
alert(document.constructor.name); // HTMLDocument
因此,document
是 HTMLDocument
類(lèi)的一個(gè)實(shí)例。
它位于 DOM 層次結(jié)構(gòu)(hierarchy)中的什么位置?
是的,我們可以瀏覽規(guī)范,但是手動(dòng)找出它會(huì)更快。
我們通過(guò) __proto__
來(lái)遍歷原型鏈。
正如我們所知道的,類(lèi)的方法在 constructor 的 prototype
中。例如,HTMLDocument.prototype
有用于文檔(document)的方法。
此外,在 prototype
中還有一個(gè)對(duì)構(gòu)造函數(shù)的引用:
alert(HTMLDocument.prototype.constructor === HTMLDocument); // true
為了以字符串的形式獲取一個(gè)類(lèi)的 name,我們可以使用 constructor.name
。讓我們對(duì)整個(gè) document
的原型鏈執(zhí)行該操作,直到 class Node
:
alert(HTMLDocument.prototype.constructor.name); // HTMLDocument
alert(HTMLDocument.prototype.__proto__.constructor.name); // Document
alert(HTMLDocument.prototype.__proto__.__proto__.constructor.name); // Node
這就是層次結(jié)構(gòu)。
我們還可以使用 console.dir(document)
來(lái)檢查對(duì)象,并通過(guò)打開(kāi) __proto__
來(lái)查看這些名稱(chēng)??刂婆_(tái)將它們從 constructor
內(nèi)部取出來(lái)。
更多建議: