2010-04-08 131 views
64

它的錯誤我,我不能只是做document.querySelectorAll(...).map(...)甚至在Firefox 3.6中,我仍然無法找到答案,所以我想我會在SO從這個博客的問題交叉後:爲什麼document.querySelectorAll返回一個StaticNodeList而不是真正的Array?

http://blowery.org/2008/08/29/yay-for-queryselectorall-boo-for-staticnodelist/

有沒有人知道爲什麼你沒有獲得數組的技術原因?或者爲什麼一個StaticNodeList不能以一種可以使用map,concat等的方式從一個數組繼承?

(順便說一句,如果它只是一個你想要的,你可以這樣做NodeList.prototype.map = Array.prototype.map; ......但同樣的功能,這是爲什麼功能(故意?)擋在首位?)

+0

其實也的getElementsByTagName不返回數組,而是一個集合,如果你想像一個數組一樣使用它(使用像concat等方法),你必須通過循環將這個集合轉換成一個數組,然後複製每個ele將集合集合到Array中。沒有人抱怨過這件事。 – 2010-08-16 12:02:29

回答

54

我相信這是W3C的哲學決定。 W3C DOM [spec]的設計與JavaScript的設計非常正交,因爲DOM是意味着是平臺和語言中立的。

決定,比如「getElementsByFoo()返回有序NodeList」或「querySelectorAll()返回StaticNodeList」是非常有意的,這樣的實現不必擔心基於語言相關的實施使企業的返回的數據結構(如.map是可在JavaScript和Ruby的數組中使用,但在C#中的列表中使用而不使用)。

W3C的目標低:他們會說NodeList應該包含readonly .length property of type unsigned long,因爲他們相信每一個執行至少可以支持,但他們不會明確地說,[]指數運營商應該被重載以支持越來越因爲他們不想妨礙一些可憐的小語言,它想要實現getElementsByFoo()但不支持運算符重載。這是大部分規範中普遍存在的哲學。

約翰Resig的有voiced a similar option是你的,到he adds

我的論點是沒有這麼多, NodeIterator是不是很類似DOM是 ,這是不是很JavaScript的等。它 並不需要的功能 存在於JavaScript語言優勢和 用它們來儘自己最大的能力...

我有點同情。如果DOM是專門用JavaScript特性編寫的,那麼使用它就不那麼笨拙和直觀。同時我也瞭解W3C的設計決策。

+0

謝謝,這有助於理解情況。 – Kev 2010-04-08 16:20:57

+0

@Kev:我在該博客文章頁面看到您的評論,質疑您如何將「StaticNodeList」轉換爲數組。我認可@ mck89的答案是將'NodeList' /'StaticNodeList'轉換爲本地數組的方式,但是在IE(8 obv)中會出現JScript錯誤,因爲這些對象被託管/「特殊」 。 – 2010-04-08 16:38:32

+0

的確如此,這就是爲什麼我高舉他的原因。儘管其他人已經取消了我的+1。託管/特別是什麼意思? – Kev 2010-04-13 20:45:22

34

我不知道爲什麼它返回一個節點列表而不是一個數組,可能是因爲像getElementsByTagName它會在更新DOM時更新結果。反正一個很簡單的方法來變換結果在一個簡單的數組是:

Array.prototype.slice.call(document.querySelectorAll(...)); 

,然後你可以這樣做:

Array.prototype.slice.call(document.querySelectorAll(...)).map(...); 
+3

事實上,當你更新DOM時它不會更新結果 - 因此是'靜態'。您必須再次手動調用qSA才能更新結果。 「切片」行+1。 – Kev 2010-04-08 14:32:55

+0

是的,就像Kev說的那樣:qSA結果集是靜態的,getElementsByTagName()結果集是動態的。 – 2015-07-23 12:35:28

+1

這是ie8安全嗎? – SuperUberDuper 2015-10-16 13:15:36

9

我想補充一下新月說,

,如果這是你想要的,你可以這樣做NodeList.prototype.map = Array.prototype.map

唐只是一個功能不要這樣做!它一點也不保證工作。

沒有的JavaScript或DOM/BOM標準規定,NodeList構造函數,即使存在作爲全球/ window財產,或由querySelectorAll返回的NodeList將繼承它,或者它的原型是可寫的,或者說功能Array.prototype.map實際上將在NodeList上工作。

允許NodeList成爲'主機對象'(在IE和一些較舊的瀏覽器中是一個)。 Array方法被定義爲允許在任何暴露數字和length屬性的JavaScript'本地對象'上運行,但它們不需要在主機對象上工作(而在IE中,它們不這樣做)。

令人討厭的是,你沒有得到DOM列表上的所有數組方法(所有這些方法,不只是StaticNodeList),但沒有可靠的方法。你必須你回來手動陣列的每個DOM列表轉換:

Array.fromList= function(list) { 
    var array= new Array(list.length); 
    for (var i= 0, n= list.length; i<n; i++) 
     array[i]= list[i]; 
    return array; 
}; 

Array.fromList(element.childNodes).forEach(function() { 
    ... 
}); 
+0

拍攝,我沒有想到這一點。謝謝! – Kev 2010-04-28 19:15:11

+0

我同意+1。只是一個評論,我認爲做「var array = []」而不是「var array = new Array(list.length)」wouild使代碼更短。但如果你知道這樣做可能有問題,我很感興趣。 – 2010-08-16 12:24:55

+0

@MarcoDemaio:不,沒問題。 'new Array(n)'只是給JS terp一個提示陣列最終結束的提示。這可能允許它提前分配這些空間,這可能會導致加速,因爲隨着陣列的增長,可以避免一些內存重新分配。我不知道它在現代瀏覽器中是否確實有幫助,但是我會懷疑是不可測量的。 – bobince 2010-08-16 12:56:33

70

您可以使用ES2015(ES6)spread operator

[...document.querySelectorAll('div')]

將StaticNodeList轉化爲項目的數組。

這是一個關於如何使用它的例子。

[...document.querySelectorAll('div')].map(x => console.log(x.innerHTML))
<div>Text 1</div> 
 
<div>Text 2</div>

0

我想你可以簡單地做以下

Array.prototype.map.call(document.querySelectorAll(...), function(...){...}); 

它的工作非常適合我

相關問題