为什么document.querySelectorAll返回一个StaticNodeList而不是一个真正的数组?

它让我感到害怕,即使在Firefox 3.6中我也不能只做document.querySelectorAll(...).map(...) ,但我仍然无法find答案,所以我想我会交叉发布所以从这个博客的问题:

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

有没有人知道为什么你没有一个arrays的技术原因? 或者为什么一个StaticNodeList不能以一种可以使用mapconcat等的方式从一个数组inheritance?

(顺便说一句,如果它只是你想要的一个函数,你可以做一些像NodeList.prototype.map = Array.prototype.map; …但是,为什么这个function(故意?)阻止在第一位?

我相信这是W3C的哲学决定。 W3C DOM [spec]的devise与JavaScript的devise非常正交,因为DOM是平台和语言中立的。

像“ getElementsByFoo()返回一个有序的NodeList ”或“ querySelectorAll()返回一个StaticNodeList ”的决定是非常有意义的,所以实现不必担心基于语言相关的实现(如.map在JavaScript和Ruby中可用于数组,但不在 C#中的列表中)。

W3C的目标很低:他们会说一个NodeList应该包含一个types为unsigned long的只读.length属性,因为他们相信每个实现至less可以支持这个 ,但是他们不会明确地说[] index操作符应该被重载支持获取位置元素,因为他们不想妨碍一些可怜的小语言来实现getElementsByFoo()但不能支持运算符重载。 这是大部分规范中stream行的哲学。

约翰·雷西格(John Resig)已经提出了和你一样的select , 他补充说 :

我的观点不是那么多, NodeIterator不是非常类似于DOM的,它不像JavaScript那样。 它没有利用JavaScript语言中的特性,并尽其所能地使用它们。

我有些同情。 如果DOM是专门用JavaScript特性编写的,那么使用起来会比较麻烦,而且更直观。 同时我也了解W3C的devise决策。

您可以使用ES2015(ES6) 扩展运算符 :

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

将把StaticNodeList转换为项目数组。

这里是一个如何使用它的例子。

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

我不知道为什么它会返回一个节点列表,而不是一个数组,也许是因为像getElementsByTagName它会更新DOM时更新结果。 无论如何,一个非常简单的方法来转换一个简单的数组结果是:

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

然后你可以这样做:

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

只要加上Crescent所说的话,

如果它只是一个你想要的function,你可以做一些像NodeList.prototype.map = Array.prototype.map

不要这样做! 一点也不保证工作。

没有JavaScript或DOM / BOM标准指定NodeList构造函数甚至作为全局/ window属性存在,或者由querySelectorAll返回的NodeList将从其inheritance,或者其原型是可写的,或者函数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() { ... });