摘要:小結(jié)這下我們可以得出結(jié)論了個(gè)屬性返回的對(duì)象不止能遍歷到子元素,還能遍歷到來自其原型的三個(gè)屬性。既要防止那些添加修改了原型屬性的對(duì)象遍歷出多余的的結(jié)果,也要防止類似這種非標(biāo)準(zhǔn)屬性返回一個(gè)屬性的枚舉性不可控的對(duì)象的坑。
問題的引出
關(guān)于DOM元素的children屬性,以前我只在意它和childNodes屬性的區(qū)別:即children屬性只會(huì)返回子元素節(jié)點(diǎn)集合,而childNodes返回的就不止元素節(jié)點(diǎn),還有文本節(jié)點(diǎn)等所有子節(jié)點(diǎn)集合。這樣看來,children似乎是我們獲取子元素而舍棄其他類型的子節(jié)點(diǎn)的最佳選擇,雖然說在IE8-的瀏覽器下用它還會(huì)返回注釋節(jié)點(diǎn),但兼容起來也是很簡(jiǎn)單的。
我們知道,children返回的子元素集合實(shí)際上是一個(gè)類似數(shù)組的HTMLCollection對(duì)象,那接下來我們要獲取每個(gè)子元素自然要遍歷它咯,但是一遍歷,問題就出來了:
上面的代碼使用了for-in進(jìn)行遍歷,但我們預(yù)料中的結(jié)果并未出現(xiàn),以chrome為例,運(yùn)行結(jié)果是這個(gè):
這就是我在前面的原生js練習(xí)題-第一課那篇文章中提到過的坑了,其中有兩個(gè)奇怪的問題:
多返回了length等幾個(gè)在數(shù)組應(yīng)該是不可枚舉的屬性。
把有id的元素重復(fù)了兩次。
關(guān)于問題1先說一下,上面提到的第一點(diǎn)在各個(gè)瀏覽器里情況都相同,而第二點(diǎn)關(guān)于返回的元素是否重復(fù)在各瀏覽器下情況還不同。
我們先討論第一點(diǎn),這里要考慮for-in循環(huán)遍歷對(duì)象時(shí)的規(guī)則比較奇葩:對(duì)象自身和繼承到的可枚舉屬性都會(huì)被遍歷到。所以為確定多遍歷到的內(nèi)容到底是自身還是原型上的屬性,我們來驗(yàn)證一下:
console.log(Object.keys(o)); //["0","1","2","i","ii"] console.log(Object.getOwnPropertyNames(o)); //["0","1","2","i","ii"]
Object.keys()方法返回的是可枚舉的自身屬性的屬性名組成的數(shù)組,而Object.getOwnPropertyNames()返回的是所有自身屬性的屬性名組成的數(shù)組(含可枚舉和不可枚舉)。在這里我們沒有看到length、item()、namedItem()三個(gè)屬性的身影,由此斷定他們不是HTMLCollection對(duì)象自身的屬性,但既然能被for-in遍歷到那就只能是來自HTMLCollection原型的可枚舉屬性。我們可以用Object.getOwnPropertyDescriptor()來驗(yàn)證其在原型上的可枚舉性:
console.log(Object.getOwnPropertyDescriptor(o.__proto__, "length").enumerable); //true console.log(Object.getOwnPropertyDescriptor(o.__proto__, "item").enumerable); //true console.log(Object.getOwnPropertyDescriptor(o.__proto__, "namedItem").enumerable); //true關(guān)于問題2
解決了多出來的三個(gè)屬性的來源,我們?cè)倩剡^頭看看為什么會(huì)把有id的元素重復(fù)了兩次。觀察用Object.keys()方法返回的數(shù)組,這兩次一次用下標(biāo)做屬性名、一次用id名作屬性名。但其實(shí)兩個(gè)屬性名指向的是同一個(gè)對(duì)象:
o[0]===o["i"] //true o[1]===o["ii"] //true
可見之所以for-in會(huì)把id的元素重復(fù)遍歷兩次,不是因?yàn)橛衖d的元素都添加進(jìn)HTMLCollection對(duì)象兩次,只是一個(gè)元素有了兩個(gè)屬性名而已,這是chrome的情況(我的版本是48.0.2564.116 m),但放到火狐和IE下結(jié)果卻還有點(diǎn)所不同:
//FF console.log(Object.keys(o)); //["0", "1", "2"] console.log(Object.getOwnPropertyNames(o)); // ["0", "1", "2", "i", "ii"] o[0]===o["i"] //true o[1]===o["ii"] //true //IE11 console.log(Object.keys(o)); //["i", "ii", "2"] console.log(Object.getOwnPropertyNames(o)); // ["i", "ii", "2"] o[0]===o["i"] //true o[1]===o["ii"] //true
可見雖然不同的瀏覽器返回的HTMLCollection對(duì)象都存在有id的子元素有兩個(gè)屬性名的情況,但從Object.keys(o)的結(jié)果看,火狐和IE對(duì)同一元素默認(rèn)只取一個(gè)屬性名。所以如果你在火狐或IE運(yùn)行一開始那段代碼,就會(huì)發(fā)現(xiàn)for-in遍歷時(shí)火狐和IE也只對(duì)同一元素訪問一次,不會(huì)像chrome那樣重復(fù)遍歷。然而我們還看到,這兩個(gè)瀏覽器間選取的屬性名也不同,火狐優(yōu)先選擇下標(biāo)形式,而IE優(yōu)先選擇id形式,同時(shí)由Object.getOwnPropertyNames(o)的結(jié)果我們還可以窺探出火狐實(shí)現(xiàn)選取屬性名的機(jī)制可能是通過將id形式的屬性名設(shè)為不可枚舉來實(shí)現(xiàn)的,至于IE就不清楚了。
小結(jié)這下我們可以得出結(jié)論了:children個(gè)屬性返回的HTMLCollection對(duì)象不止能遍歷到子元素,還能遍歷到來自其原型的length、item()、namedItem()三個(gè)屬性。而且一旦遍歷到的子元素有id,就存在HTMLCollection對(duì)象里一個(gè)元素會(huì)有兩個(gè)屬性名的問題,更讓人蛋疼的是各瀏覽器對(duì)這兩個(gè)屬性名的選取各不相同。當(dāng)然,最根本原因還是因?yàn)?b>children屬性現(xiàn)在還沒被正式納入標(biāo)準(zhǔn),在使用這種非標(biāo)準(zhǔn)屬性時(shí)我們難免遇到一些奇葩的狀況。
所以這也告誡我們,如果對(duì)一個(gè)非標(biāo)準(zhǔn)屬性的特點(diǎn)不是特別了解,還是不要輕易使用它,否則出現(xiàn)的問題往往是你難以控制的。但如果你還是覺得children使用起來方便,那在使用時(shí)就得謹(jǐn)記這些問題,比如在遍歷子元素時(shí)最好放棄for-in循環(huán),老老實(shí)實(shí)使用基本的for循環(huán)去遍歷數(shù)字索引吧,這樣就和遍歷數(shù)組差不多,不會(huì)遍歷到那些多出來的屬性了。
而至于for-in,最好只用來遍歷數(shù)組或簡(jiǎn)單的對(duì)象。既要防止那些添加、修改了原型屬性的對(duì)象遍歷出多余的的結(jié)果,也要防止類似children這種非標(biāo)準(zhǔn)屬性返回一個(gè)屬性的枚舉性不可控的對(duì)象的坑。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/87704.html
摘要:直到內(nèi)部的全部循環(huán)結(jié)束為止,才進(jìn)入下一個(gè)元素,當(dāng)循環(huán)結(jié)束時(shí),內(nèi)部的節(jié)點(diǎn)都已經(jīng)生成好了。 自己實(shí)現(xiàn)虛擬 DOM 從 HTML 中提煉數(shù)據(jù)結(jié)構(gòu) 先來看下我們的 HTML 傅雷家書 讀家書,想付雷 從 HTML 中我們可以抽離出它的數(shù)據(jù)結(jié)構(gòu): 首先頁(yè)面中只需要一個(gè)根節(jié)點(diǎn)root,定義為:nodesDate數(shù)組 root內(nèi)有兩個(gè)子元素h1和span,數(shù)組有兩項(xiàng),每項(xiàng)為內(nèi)...
摘要:一個(gè)表達(dá)式是由一個(gè)或多個(gè)被分割的定位步組成。對(duì)于此類斷言,我們可以使用謂詞根據(jù)額外的遍歷樹來過濾出符合條件的節(jié)點(diǎn)。所以用來做一些低水平或與應(yīng)用無關(guān)的事情遍歷樹來找指定屬性的節(jié)點(diǎn)讓人蛋疼。這是一個(gè)專門用來讓你使用簡(jiǎn)潔的慣用表達(dá)式來遍歷的工具。 編者注: XPath 即為XML路徑語言(XML Path Language),它是一種用來確定XML文檔中某部分位置的語言。 XPat...
摘要:一個(gè)表達(dá)式是由一個(gè)或多個(gè)被分割的定位步組成。對(duì)于此類斷言,我們可以使用謂詞根據(jù)額外的遍歷樹來過濾出符合條件的節(jié)點(diǎn)。所以用來做一些低水平或與應(yīng)用無關(guān)的事情遍歷樹來找指定屬性的節(jié)點(diǎn)讓人蛋疼。這是一個(gè)專門用來讓你使用簡(jiǎn)潔的慣用表達(dá)式來遍歷的工具。 編者注: XPath 即為XML路徑語言(XML Path Language),它是一種用來確定XML文檔中某部分位置的語言。 XPat...
摘要:展開的屬性后發(fā)現(xiàn),繼承于一個(gè)對(duì)象,而這個(gè)對(duì)象又繼承于對(duì)象。這證實(shí)了我們對(duì)的猜想。是比較新的模型,相比更加完善,不光有元素,還有節(jié)點(diǎn)和。關(guān)于,和的關(guān)系,就是長(zhǎng)得像,有個(gè)別相似的功能,但是是完全不一樣的東西。 Array,NodeList, HTMLCollection這三個(gè)概念和它們之間的關(guān)系有很多做了幾年前端的同學(xué)都搞不清楚,經(jīng)常遇到但是又感覺很陌生,剪不斷理還亂的感覺。今天咱們就來理...
閱讀 1669·2021-11-19 09:40
閱讀 2926·2021-09-24 10:27
閱讀 3215·2021-09-02 15:15
閱讀 1876·2019-08-30 15:54
閱讀 1202·2019-08-30 15:54
閱讀 1369·2019-08-30 13:12
閱讀 626·2019-08-28 18:05
閱讀 2794·2019-08-27 10:53