Javascript 为什么 document.querySelectorAll 返回一个 StaticNodeList 而不是一个真正的数组?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2600343/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why does document.querySelectorAll return a StaticNodeList rather than a real Array?
提问by Kev
It bugs me that I can't just do document.querySelectorAll(...).map(...)even in Firefox 3.6, and I still can't find an answer, so I thought I'd cross-post on SO the question from this blog:
document.querySelectorAll(...).map(...)即使在 Firefox 3.6中我也无法做到这一点,这让我很烦恼,而且我仍然找不到答案,所以我想我会在 SO 上交叉发布此博客中的问题:
http://blowery.org/2008/08/29/yay-for-queryselectorall-boo-for-staticnodelist/
http://blowery.org/2008/08/29/yay-for-queryselectorall-boo-for-staticnodelist/
Does anyone know of a technical reason why you don't get an Array? Or why a StaticNodeList doesn't inherit from an Array in such a way that you could use map, concat, etc?
有谁知道为什么你没有得到一个数组的技术原因?或者为什么StaticNodeList不会以这样的方式数组继承,你可以使用map,concat等等?
(BTW if it's just one function you want, you can do something like NodeList.prototype.map = Array.prototype.map;...but again, why is this functionality (intentionally?) blocked in the first place?)
(顺便说一句,如果它只是你想要的一个功能,你可以做类似的事情NodeList.prototype.map = Array.prototype.map;......但同样,为什么这个功能(故意?)首先被阻止?)
采纳答案by Crescent Fresh
I believe it to be a philosophical decision of the W3C. The design of the W3C DOM [spec] is quite orthogonal to the design of JavaScript, as the DOM is meantto be platform and language neutral.
我相信这是 W3C 的哲学决定。W3C DOM [规范] 的设计与 JavaScript 的设计非常正交,因为 DOM旨在与平台和语言无关。
Decisions like "getElementsByFoo()returns an ordered NodeList" or "querySelectorAll()returns a StaticNodeList" are very much intentional, so that implementations don't have to worry about aligning their returned data structure based on language-dependent implementations (like .mapbeing available on Arrays in JavaScript and Ruby, but noton Lists in C#).
诸如“getElementsByFoo()返回有序NodeList”或“querySelectorAll()返回一个StaticNodeList”之类的决定是非常有意的,因此实现不必担心根据依赖于语言的实现来对齐其返回的数据结构(例如.map在 JavaScript 和 Ruby 中的数组上可用,但是不在C# 中的列表中)。
The W3C aim low: they'll say a NodeListshould contain a readonly .lengthproperty of type unsigned longbecause they believe every implementation can at least support that, but they won't say explicitly that the []index operator should be overloaded to support getting positional elements, because they don't want to stymie some poor little language that comes along that wants to implement getElementsByFoo()but cannot support operator overloading. It's a prevalent philosophy present throughout much of the spec.
W3C 的目标很低:他们会说 aNodeList应该包含一个unsigned long 类型的只读.length属性,因为他们相信每个实现至少可以支持它,但他们不会明确说[]应该重载索引运算符以支持获取位置元素,因为他们不想阻碍一些想要实现getElementsByFoo()但不能支持运算符重载的可怜的小语言。这是贯穿大部分规范的普遍哲学。
John Resig has voiced a similar optionas yours, to which he adds:
My argument isn't so much that
NodeIteratorisn't very DOM-like it's that it isn't very JavaScript-like. It doesn't take advantage of the features present in the JavaScript language and use them to the best of its ability...
我的论点并不是很像
NodeIteratorDOM,而是不太像 JavaScript。它没有利用 JavaScript 语言中存在的功能,而是尽其所能地使用它们......
I do somewhat empathize. If the DOM was written specifically with JavaScript features in mind it would be a lot less awkward and more intuitive to use. At the same time I do understand the W3C's design decisions.
我倒是有些感同身受。如果 DOM 是专门用 JavaScript 特性编写的,那么使用起来就会少很多尴尬和直观。同时,我确实了解 W3C 的设计决策。
回答by Vlad Bezden
You can use ES2015 (ES6) spread operator:
您可以使用 ES2015 (ES6)扩展运算符:
[...document.querySelectorAll('div')]
[...document.querySelectorAll('div')]
will convert StaticNodeList to Array of items.
将 StaticNodeList 转换为项目数组。
Here is an example on how to use it.
这是有关如何使用它的示例。
[...document.querySelectorAll('div')].map(x => console.log(x.innerHTML))
<div>Text 1</div>
<div>Text 2</div>
回答by mck89
I don't know why it returns a node list instead of an array, maybe because like getElementsByTagName it will update the result when you update the DOM. Anyway a very simple method to transform that result in a simple array is:
我不知道为什么它返回一个节点列表而不是一个数组,可能是因为像 getElementsByTagName 一样,它会在您更新 DOM 时更新结果。无论如何,将结果转换为简单数组的一种非常简单的方法是:
Array.prototype.slice.call(document.querySelectorAll(...));
and then you can do:
然后你可以这样做:
Array.prototype.slice.call(document.querySelectorAll(...)).map(...);
回答by bobince
Just to add to what Crescent said,
只是为了补充新月所说的,
if it's just one function you want, you can do something like NodeList.prototype.map = Array.prototype.map
如果它只是你想要的一个函数,你可以做一些类似 NodeList.prototype.map = Array.prototype.map 的事情
Don't do this!It's not at all guaranteed to work.
不要这样做!它根本不能保证工作。
No JavaScript or DOM/BOM standard specifies that the NodeListconstructor-function even exists as a global/windowproperty, or that the NodeListreturned by querySelectorAllwill inherit from it, or that its prototype is writable, or that the function Array.prototype.mapwill actually work on a NodeList.
没有 JavaScript 或 DOM/BOM 标准指定NodeList构造函数甚至作为全局/window属性存在,或者NodeList返回的 byquerySelectorAll将从它继承,或者它的原型是可写的,或者该函数Array.prototype.map实际上可以在 NodeList 上工作。
A NodeList is allowed to be a ‘host object' (and is one, in IE and some older browsers). The Arraymethods are defined as being allowed to operate on any JavaScript ‘native object' that exposes numeric and lengthproperties, but they're not required to work on host objects (and in IE, they don't).
NodeList 被允许是一个“主机对象”(在 IE 和一些旧浏览器中是一个)。这些Array方法被定义为允许对任何公开数字和length属性的JavaScript“本机对象”进行操作,但它们不需要在宿主对象上工作(在 IE 中,它们不需要)。
It's annoying that you don't get all the array methods on DOM lists (all of them, not just StaticNodeList), but there's no reliable way round it. You'll have to convert every DOM list you get back to an Array manually:
令人讨厌的是,您没有获得 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() {
...
});
回答by Max Leps
I think you can simply do following
我认为您可以简单地执行以下操作
Array.prototype.map.call(document.querySelectorAll(...), function(...){...});
It works perfect for me
它非常适合我
回答by vsync
This is an option I wanted to add to the range of other possibilities suggested by others here. It's meant for intellectual fun only and is not advised.
这是我想添加到其他人在这里建议的其他可能性范围中的一个选项。它仅用于智力乐趣,不建议使用。
Just for the funof it, here's a way to "force" querySelectorAllto kneel down and bow to you:
只是为了好玩,这里有一种“强迫”querySelectorAll向你跪下鞠躬的方法:
Element.prototype.querySelectorAll = (function(QSA){
return function(){
return [...QSA.call(this, arguments[0])]
}
})(Element.prototype.querySelectorAll);
Now it feels good to step all over that function, showing it who's the boss.
Now I don't know what's better, creating a whole new namedfunction wrapper and then have all your code use that weird name (pretty much jQuery-style) or override the function like above once so the rest of your code would still be able to use the original DOM method name querySelectorAll.
现在感觉很好地完成了这个功能,向它展示谁是老板。现在我不知道什么更好,创建一个全新的命名函数包装器,然后让所有代码使用那个奇怪的名称(几乎是 jQuery 风格)或像上面那样覆盖一次函数,这样其余的代码仍然可以使用原始 DOM 方法名称querySelectorAll。
- Such approach would eliminate possible use of sub-methods
- 这种方法将消除可能使用的子方法
I wouldn't recommend this in any way, unless you honestly don't give a [you know what].
我不会以任何方式推荐这个,除非你老实说不给[你知道]。

