先聲明一下DOM2中NodeIterator和TreeWalker這兩類型真的只是用來玩玩的,因為性能不行遍歷起來超級慢,在JS中基本用不到它們,除了《高程》上有兩三頁對它的講解外,谷歌的學習資料也是甚少(倒是有挺多國外文章)...由於本着不放過任何知識的態度,結合着自己的理解學習了下這兩玩意,你們對這兩東西了解了解就好~
DOM2級遍歷和范圍模塊定義了兩個用於完成順序遍歷DOM結構的類型:NodeIterator和TreeWalker。這兩類型基於給定起點對DOM結構執行深度優先先序遍歷,兼容性>IE8和高版本其他瀏覽器可訪問。
NodeIterator:
使用document.createNodeIterator(root, whatShow, filter, entityReferenceExpansion)創建NoedIterator類型實例iterator,所以原型鏈關系為:
iterator.__proto__->NodeIterator.prototype->Object.prototye
TreeWalker:
使用document.createTreeWalker(root, whatShow, filter, entityReferenceExpansion)創建TreeWalker類型實例walker,所以原型鏈關系為:
walker.__proto__->TreeWalker.prototype->Object.prototype
對比下來,其實就兩個方法常用,nextNode()和previousNode()。TreeWalker.prototype比NodeIterator.prototype多了一些在不同方向上遍歷的方法也就沒什么了。
(1).在每個iterator中有一個內部指針指向根節點,nextNode方法是返回遍歷器內部指針所在節點,然后會將指針移向下一個節點。previousNode()方法是先將指針移向上一個節點,然后返回該節點。所以nextNode()==previousNode()
(2).在每個walker中也有一個內部指針,但是指向根節點的第一個子節點,nextNode方法是返回遍歷器所在節點然后並不移動指針(就是說指針和節點在同一處),previous()方法是先將指針移向上一個節點,然后返回該節點,所以這里nextNode() != previous()
所以TreeWalker.prototype就有一個currentNode屬性,表示在上一次遍歷中返回的節點:
(3).區別說完,說說它兩的參數都是相同的:
root:想要作為搜索起點的樹中的節點
whatToShow:表示要訪問哪些節點的數字代碼,來自NodeFilter.prototype還是NodeFilter自身上中的這些大寫常量...
filter:是NodeFilter類型實例對象,或者是一個表示應該接受還是拒絕某種特定節點的函數。作用是當調用nextNode或previousNode時候要經過這個過濾器來刪選想要的節點,如果說文檔中任何一個節點走一步,那么根據篩選節點類型不同每次返回的節點實際上可能走了好幾步。
entityReferenceExpansion:表示是否要擴展實體引用,false就好。
對了,NodeIterator和TreeWalker還有一點區別就是在使用NodeIterator對象時,NodeFilter.FILTER_SKIP和NodeFilter.FILTER_REJECT作用相同跳過指定節點。在使用TreeWalker對象時,NodeFilter.FILTER_SKIP會跳過相應節點繼續前進到子樹中下一個節點,NodeFilter.FILTER_REJECT會相應節點及該節點的整個子樹。
OK!說完了上面的,也不知道大家有沒有懂~不懂沒關系反正這兩類型也不常用,效率也差~
通過DOM遍歷的這兩類型很容易讓人想到我們常用的document.getElementById,document.getElementsByTagName,document.getElementsByNames...系列方法不是也是在DOM中搜尋指定節點的么...這里用NodeIterator來實現一下
Document.prototype.getElementById = function(id){ var filter = function(node){ return node.id == id ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP; } var iterator = document.createNodeIterator(document, NodeFilter.SHOW_ELEMENT, filter, false); var node = iterator.nextNode(); return node; } Document.prototype.getElementsByTagName = function(tagname){ var filter = function(node){ return node.tagName.toLowerCase() == tagname ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP; } var htmlcollection = []; var iterator = document.createNodeIterator(document, NodeFilter.SHOW_ELEMENT, filter, false); var node = iterator.nextNode(); while(node!=null){ htmlcollection.push(node); node = iterator.nextNode(); } htmlcollection.__proto__ = HTMLCollection.prototype; return htmlcollection; }
成功!其他的方法類似的,感興趣可以自行實現~
stackoverflow里有人提出了When to use NodeIterator? 把querySelector和filter過濾進行比較,這誰快誰慢光看名字就顯而易見嘛,感興趣可以看看啊~我粗略測試下
也不知道人家JS引擎中getElementById真正是怎么實現的,改天抽空看看~
參考
《JavaScript高級程序設計》