为什么document.querySelectorAll返回一个StaticNodeList而不是一个真正的数组?
它让我感到害怕,即使在Firefox 3.6中我也不能只做document.querySelectorAll(...).map(...)
,但我仍然无法find答案,所以我想我会交叉发布所以从这个博客的问题:
http://blowery.org/2008/08/29/yay-for-queryselectorall-boo-for-staticnodelist/
有没有人知道为什么你没有一个arrays的技术原因? 或者为什么一个StaticNodeList不能以一种可以使用map
, concat
等的方式从一个数组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() { ... });