Почему нодлисты содержат лишние неопределенные элементы, которые не отражаются в его свойстве длины?

Фон:


При работе со списком узлов я столкнулся с очень странным явлением. Я хотел использовать getElementsByClassName или что-то подобное, а затем отсортировать его. Я решил, что одним из способов было бы перебрать список узлов и поместить каждый элемент в массив и отсортировать массив. (Кстати, это сработало, но не так, как ожидалось). Я попытался использовать for (var i in nodeList) для итерации, но он продолжал генерировать исключение для последних нескольких элементов, которые не были определены. странно то, что я мог бы вместо этого использовать for (var i = 0; i < nodeList.length; i++) для итерации. Я просто протестировал его снова и на странице stackoverflow я запустил в своей консоли следующий код:

for (var i in document.getElementsByTagName("span"))
    console.count("items");
console.log(document.getElementsByTagName("span").length);

Было отсчитано до items: 382, но длина дала 380. Как и ожидалось, когда я ввел document.getElementsByTagName("span")[380] и document.getElementsByTagName("span")[381], они вернулись неопределенными. Это странное поведение не происходит с массивами (разрешено, списки узлов и массивы отличаются, но это доказывает, что это не разные циклы for, вызывающие проблему).

вопрос:


Почему конструкции for(var i in nodeList) по-разному ведут себя в списках узлов, возвращая в конце пару неопределенных элементов?


person Joseph Marikle    schedule 16.09.2011    source источник
comment
Объекты NodeList подобны массивам. Следовательно, не выполняйте итерацию с for in, а с for.   -  person Šime Vidas    schedule 17.09.2011
comment
Кстати, вы можете преобразовать объект NodeList в обычный массив следующим образом: [].slice.call( nodelist ).   -  person Šime Vidas    schedule 17.09.2011
comment
Спасибо @ Šime Vidas, но почему? Что вызывает такое странное поведение? for in просто не работает для объектов?   -  person Joseph Marikle    schedule 17.09.2011
comment
Я вложил в свой ответ объяснение ...   -  person Šime Vidas    schedule 17.09.2011
comment
Еще раз спасибо, Шиме Видас. Теперь это имеет смысл. :П   -  person Joseph Marikle    schedule 17.09.2011


Ответы (2)


Два дополнительных свойства, которые улавливает оператор итерации for in:

  • длина
  • элемент

Приведу простой пример. Допустим, на странице есть 3 элемента SPAN.

var spans = document.getElementsByTagName( 'span' );

Теперь spans - это объект NodeList, который содержит 5 свойств:

  • 0
  • 1
  • 2
  • длина
  • элемент

Первые 3 свойства являются индексами и содержат ссылки на эти элементы SPAN. Два других свойства - длина и элемент - являются двумя дополнительными свойствами. Все объекты NodeList имеют эти два свойства.

Оператор for in выполняет итерацию по всем 5 свойствам объекта NodeList, что, вероятно, не то, что вам нужно. Поэтому используйте обычный оператор for.

var i, span;

for ( i = 0; i < spans.length; i++ ) {
    span = spans[i];
    // do stuff with span
}
person Šime Vidas    schedule 16.09.2011
comment
Это имеет смысл. Большое спасибо за объяснение. Теперь знаю, в следующий раз. : P Также спасибо за фрагмент кода о том, как преобразовать NodeList в массив. Это будет очень полезно в будущем. - person Joseph Marikle; 17.09.2011
comment
@Joseph Обратите внимание, что этот хакерский прием не работает в IE8, я думаю. Меня не волнует IE8, поэтому мои ответы и советы всегда следует применять осторожно :) - person Šime Vidas; 17.09.2011
comment
lol Я ценю это в любом случае. XD Я всегда ищу такие маленькие хитрости. - person Joseph Marikle; 17.09.2011

for-in выполняет итерацию по всем перечислимым свойствам объекта, таким как длина и элемент (это ваша ситуация). Отсюда еще два результата. Он также перечислит все, что добавлено к прототипу объекта.

for перебирает числовые индексы и не принимает во внимание перечислимые свойства. Поэтому гораздо надежнее использовать прежний способ.

person dfsq    schedule 16.09.2011
comment
Спасибо за ваш ответ и краткое определение. - person Joseph Marikle; 17.09.2011