在 JavaScript 中合并两个对象(NodeList)数组

Posted

技术标签:

【中文标题】在 JavaScript 中合并两个对象(NodeList)数组【英文标题】:Merging two object (NodeList) arrays in JavaScript 【发布时间】:2013-01-17 21:16:52 【问题描述】:

我正在尝试合并两个对象数组,以便验证表单。在这种情况下,通常的 concat 方法似乎不起作用。 Concat 适用于普通的数值和字符串数组,但不适用于对象数组。 var allTags = allInputs.concat(allSelects); 行不行。

var allInputs = document.getElementsByTagName("input");
alert("Inputs: " + allInputs.length);

var allSelects = document.getElementsByTagName("select");
alert("Selects: " + allSelects.length);

var allTags = allInputs.concat(allSelects);
alert("allTags: " + allTags.length);

【问题讨论】:

这不是数组,它的数组像对象(NodeList) 其实就是一个NodeList,所以没有concat方法。 【参考方案1】:

Concat 适用于普通数值和字符串数组,但不适用于对象数组。

实际上是这样,但是NodeList 实例没有concat 方法,并且Array#concat 没有办法识别您想要展平这些(因为它们不是数组)。

但它仍然很容易做到(不过,请参阅下面的警告)。更改此行:

var allTags = allInputs.concat(allSelects);

var allTags = [];
allTags.push.apply(allTags, allInputs);
allTags.push.apply(allTags, allSelects);

Live Example | Source

这通过使用一点技巧来工作:Array#push 接受可变数量的元素添加到数组中,Function#apply 使用给定的 this 值调用函数(在我们的例子中,@987654335 @) 和任何类似数组的对象作为要传递给它的参数。由于NodeList 实例是类似数组的,push 很乐意将列表的所有元素推送到数组中。

Function#apply 的这种行为(不需要第二个参数真的是一个数组)在规范中非常明确定义,并且在现代浏览器中得到很好的支持。

遗憾的是,IE6 和 7 不支持上述内容(我认为它专门使用主机对象——NodeLists——作为Function#apply 的第二个参数),但是我们不应该支持它们,要么。 :-) IE8 也没有,这更有问题。 IE9 对此很满意。

如果您需要支持 IE8 及更早版本,可悲的是,我认为您会遇到一个无聊的旧循环:

var allInputs = document.getElementsByTagName('input');
var allSelects = document.getElementsByTagName('select');
var allTags = [];

appendAll(allTags, allInputs);
appendAll(allTags, allSelects);

function appendAll(dest, src) 
  var n;

  for (n = 0; n < src.length; ++n) 
    dest.push(src[n]);
  

  return dest;

Live Example | Source

确实适用于 IE8 及更早版本(以及其他)。

【讨论】:

@user2035797:不用担心,我现在还添加了一个适用于 IE8 及更早版本的示例。【参考方案2】:

document.get[something] 返回一个NodeList,它看起来与Array 非常相似,但有许多与众不同的特点,其中两个是:

不包含concat方法 项目列表实时。这意味着它们会随着文档的实时变化而变化。

如果您在将 NodeList 转换为实际数组时没有任何问题,您可以执行以下操作来达到您想要的效果:

var allInputs  = document.getElementsByTagName("input")
  , allSelects = document.getElementsByTagName("select")
;//nodeLists

var inputList  = makeArray(allInputs)
  , selectList = makeArray(allSelects)
;//nodeArrays

var combined   = inputList.concat(selectList);

function makeArray(list)
  return Array.prototype.slice.call(list);

您将失去原始 NodeList 的所有行为,包括它能够实时更新以反映对 DOM 的更改,但我的猜测是,这不是一个真正的问题。

【讨论】:

请注意,就像我在回答的第一部分中给出的类似解决方案一样,这在 IE8 或更早版本中不起作用。 (在这里试试:jsbin.com/iledok/1)sigh 至少 IE9 还不错。所以对于 IE8 及更早版本,需要一个无聊的旧循环。【参考方案3】:

您正在处理的是htmlCollections,它们是实时集合(即,当您将节点添加到 DOM 时,它会自动添加到集合中。

遗憾的是,它没有concat 方法……这是有原因的。您需要做的是将其转换为数组,从而失去其实时行为:

var allInputsArray = [].slice.call(allInputs),
    allSelectsArray = [].slice.call(allSelects),
    allFields = allInputsArray.concat(allSelectsArray);

【讨论】:

【参考方案4】:

我找到了一种按 dom 对象在页面或表单中出现的顺序收集它们的简单方法。 首先,在对象上创建一个虚拟类样式“reqd”,然后使用以下内容按照它们在页面上出现的顺序创建一个列表。只需将类添加到您希望收集的任何对象。

var allTags = document.querySelectorAll("input.reqd, select.reqd");

【讨论】:

【参考方案5】:

查看过 underscore.js 吗?你可以这样做

 var allSelectsAndInputs = _.union(_.values(document.getElementsByTagName("input")),_.values(document.getElementsByTagName("select")));

欲了解更多信息,请参阅:http://underscorejs.org/#extend

该函数适用于对象,但在此设置中有效。

【讨论】:

实际上,我正在玩它……它似乎有点不对劲。待命。 如果您喜欢,请考虑对我的回答进行投票。

以上是关于在 JavaScript 中合并两个对象(NodeList)数组的主要内容,如果未能解决你的问题,请参考以下文章

在 JavaScript 中合并两个对象(NodeList)数组

如何在 JavaScript 中将两个对象合并到同一个属性中?

如何根据javascript中的键合并和替换两个数组中的对象?

javascript合并两个json对象

javascript 数组对象根据相同属性值{key:value}合并两个对象

JavaScript之合并两个对象Objectassign