Итерируйте NodeList, используя метод forEach
Я хочу перебрать NodeList с помощью метода forEach, я погуглил метод и нашел решение, которое позволяет это сделать - преобразовать NodeList в массив:
var nodesArray = Array.prototype.slice.call(nodeList);
Но я не понимаю, почему мы использовали slice
метод?
4 ответа
Есть много способов перебора NodeList.
В общем случае не стоит превращать nodeList в Array или использовать функции Array по отношению к nodeList.
Вы можете использовать старый добрый цикл for, начните с нуля и повторяйте цикл до конца массива. Этот метод существует всегда и до сих пор регулярно используется сегодня. Этот метод несколько менее читабелен, чем другие методы, упомянутые здесь, но все сводится к тому, что вам легче писать.
for(var i = 0; i < nodeList.length; ++i) doSomething(nodeList[i]);
Некоторые люди скажут вам, что использование цикла for backwards сохранит "вычислительные циклы", что бы это ни значило на самом деле. Фактически, некоторые IDE по умолчанию преобразуют предыдущий цикл в следующую структуру.
В действительности, микрооптимизация не одобряется. Нет реальной ощутимой разницы в производительности между этим методом и другими методами, упомянутыми здесь.
for(var i = nodeList.length - 1; i > -1; --i) doSomething(nodeList[i]);
Вы можете использовать цикл while, который ожидает условный оператор в качестве своего параметра. Если NodeList.item(n)
за пределами NodeList он вернет ноль, что завершит цикл.
var i = 0;
while((node = nodeList.item(i++))) doSomething(node);
Еще один метод использования цикла for, который похож на цикл while. Среднее условие традиционного цикла for ожидает условный оператор, как и цикл while выше, так что это работает.
for(var i = 0; (node = nodeList.item(i)); i++) doSomething(node);
Вы можете использовать for... в цикле с Object.keys()
, Обратите внимание, что вы должны использовать Object.keys
при использовании цикла for... in, поскольку в противном случае он будет перебирать не перечисляемые свойства, а также перечислимые.
Метод Object.keys() возвращает массив собственных перечисляемых свойств данного объекта в том же порядке, что и цикл for... in (отличие состоит в том, что цикл for-in перечисляет свойства в цепочке прототипов как Что ж).
От: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
for(var i in Object.keys(nodeList)) doSomething(nodeList[i]);
В ES6 вы можете использовать цикл for..., извлекая функцию Iterator из Array() и применяя ее к NodeList. Это будет работать для большинства других применений объекта, если свойства перечислимы.
nodeList[Symbol.iterator] = [][Symbol.iterator];
for(node of nodeList) doSomething(node);
Также достаточно круто, в ES6 работает следующее. Здесь вы извлекаете функцию Symbol.iterator из массива и применяете ее к прототипу объекта NodeList. Таким образом, всякий раз, когда создается новый NodeList, у него всегда будет итератор, так что вам нужно будет сделать это только в начале скрипта.
(function(){
"use strict";
NodeList.prototype[Symbol.iterator] = [][Symbol.iterator];
for(let node of document.querySelectorAll('*')) document.body.innerHTML += node.tagName + ' ';
document.body.innerHTML += '<br>';
for(let node of document.querySelectorAll('*:not(body)')) document.body.innerHTML += node.tagName + ' ';
})();
Существуют варианты каждого из ранее упомянутых методов, а также, возможно, есть и другие методы, которые я здесь не описал, но методы, описанные выше, дают вам общее представление о том, как вы можете перебирать NodeList, не обманывая функции массива.
Я хочу перебрать NodeList с помощью метода forEach
Зачем? Это реальная проблема здесь. Хотя вы можете перебирать NodeList, обманывая функцию массива, полагая, что NodeList является массивом, зачем вам это нужно?
Я погуглил метод и нашел решение, которое позволяет это сделать - преобразовать NodeList в массив.
Да, это один из способов угона функции Array для итерации NodeList.
Но я не понимаю, почему мы использовали метод среза?
Array.prototype.slice()
возвращает поверхностную копию элементов из исходного массива. С помощью .call()
позволяет нам дополнить значение this
в наш NodeList для slice()
,
Array.prototype.slice.call(NodeList)
возвращает массив, представляющий поверхностную копию перечисляемых свойств объекта.
Существуют многочисленные методы захвата функций Array и обмана их, заставляя их думать, что они работают с Array, когда подают им объект.
Я лично считаю, что любой из следующих методов является ужасным кодом, но, поскольку вы спросили об этом, я включу все соответствующие функции массива, которые можно использовать в NodeList.
Array.prototype.slice.call(nodeList).forEach(doSomething);
[].slice.call(nodeList).forEach(doSomething);
Вам не нужно делать массив из NodeList, вы можете просто использовать .call()
на forEach
функционировать напрямую.
Array.prototype.forEach.call(nodeList, doSomething);
[].forEach.call(nodeList, doSomething);
Или вы можете использовать map для создания нового массива из nodeList на основе формулы, а затем выполнить итерацию NodeList с помощью forEach.
Array.prototype.map.call(nodeList, function(item) {
return item;
}).forEach(doSomething);
[].map.call(nodeList, function(item) {
return item;
}).forEach(doSomething);
Они работают, потому что Array - это тип Object, а NodeList достаточно похож на Array, так что вы можете использовать некоторые функции Array с NodeList.
Тем не менее, эти методы не следует поощрять, есть гораздо лучшие способы итерации NodeList
Наконец, если вы не хотите перехватывать какие-либо функции массива, но хотите использовать forEach, вы можете создать массив из NodeList.
var array = new Array(els.length);
for(var key in Object.keys(nodeList)) arrary[key] = nodeList[key];
array.forEach(doSomething);
Это несколько избыточно, потому что вы в основном повторяете NodeList дважды.
Здесь важно отметить, что между этими методами нет реальной ощутимой разницы в том, что касается производительности. Все эти методы будут работать с относительно одинаковой скоростью, и если вы дойдете до точки, в которой вы перебираете достаточно элементов, что это на самом деле имеет значение, вы должны спросить себя, есть ли лучший подход, который вы должны использовать для решения своей проблемы.
Что касается того, какой метод лучше, чем остальные, все зависит от того, что, по вашему мнению, легче понять, и что вы чувствуете наиболее комфортно при написании.
Результаты JSPerf
Мой личный любимый метод на данный момент, пока ES6 не станет текущим стандартом, это метод while. На мой взгляд, это самый читаемый способ итерации NodeList.
var els = document.getElementsByTagName('i'), i = 0, el;
while((el = els.item(i++))) el.innerHTML += '*';
<i>Hello</i> <i>foo</i> <i>bar</i> <i>world</i>
Итерируйте NodeList, используя метод forEach
Но я не понимаю, почему мы использовали метод среза?
Вы не должны, вы можете сделать это напрямую
Array.prototype.forEach.call(nodelist, function(value, index) {
...
});
Все эти ответы устарели. На самом деле вы можете использовать forEach
на NodeList в современных браузерах!
Так что просто используйте для каждого!
Так как slice
возвращает копию любого аргумента, подобного массиву, в качестве нового объекта массива, и это именно то, что нам нужно. Мы могли бы так же легко использовать concat
,