Зачем Array.prototype.slice.call(nodeList) для элементов DOM?

Многие библиотеки JavaScript (jQuery, Zepto), похоже, вызывают Array.prototype.slice.call для результатов querySelectorAll(), getElementsByTag или ClassName...

Прочитав много похожих вопросов/ответов на StackOverflow, я понял, что нужно преобразовать результат NodeList в настоящий массив, чтобы вы могли вызывать методы массива (slice, pop) для результатов, которые недоступны в NodeLists, но что я не делаю не понимаю почему? Обычно вам действительно не нужны срезы/выталкивания в списке узлов DOM + NodeLists уже имеют свойство длины, поэтому они все равно проходимы.

Некоторые ответы, кажется, подразумевают, что это потому, что NodeList указывает на живые объекты DOM. Но опять же, если вы преобразуете его в массив, ссылки по-прежнему указывают на живые узлы DOM — так в чем же разница?

Или это что-то еще, чего мне совершенно не хватает? Помогает ли это Zepto/jQuery каким-то образом кэшировать несколько вызовов свойств для элементов DOM? (хотя я действительно не понимаю, как это сделать, поскольку это все еще живые ссылки на DOM)


person user1768759    schedule 08.11.2012    source источник
comment
Вы правы в том, что NodeList указывает на живые объекты DOM, но это означает, что когда один из них уничтожается (или больше не является допустимым элементом для списка), он и его место удаляются из списка. Когда вы нарезаете массив, это не тот же живой список — живы сами элементы, но не сам список. Когда элемент уничтожается, его место по-прежнему сохраняется в массиве... но он не будет представлять элемент - я предполагаю, что он становится неопределенным или нулевым.   -  person Ian    schedule 08.11.2012
comment
@Ian, если массив содержит ссылку на узел DOM, тогда этот узел не может быть собранным мусором.   -  person Alnitak    schedule 08.11.2012
comment
@Alnitak Понятно, тогда я думаю, что мой комментарий будет изменен, чтобы сказать что-то вроде, его место по-прежнему сохраняется в массиве, а элемент остается, пока на него больше не будет ссылок ... тогда значение в массиве будет быть неопределенным или нулевым. Это было бы правильно?   -  person Ian    schedule 08.11.2012
comment
@Ian нет, это было бы неправильно, потому что элемент не может больше не иметь ссылок, потому что в массиве все еще есть одна!. Единственный способ избежать этого — явно установить для элемента массива что-то другое, чтобы массив больше не содержал эту ссылку,   -  person Alnitak    schedule 08.11.2012
comment
Ах, я вижу - да, спасибо, это, безусловно, правдоподобное объяснение... хотя - разве это не плохо? Я имею в виду, что это будет означать, что преобразованный массив по-прежнему будет поддерживать ссылку на элемент DOM, который потенциально был удален, тем самым помечая его как все еще используемый и сигнализируя сборщику мусора, что его не следует удалять из памяти?   -  person user1768759    schedule 08.11.2012
comment
@user1768759 user1768759 он будет удален, когда сам массив будет собран мусором.   -  person Alnitak    schedule 08.11.2012
comment
ах, ребята, вы быстрее меня отвечаете, поэтому я просто повторяюсь здесь :) ... также, кстати, кажется, что querySelectorAll() отличается тем, что возвращает статический список узлов, поэтому array.prototype.slice.call делает еще меньше смысл там   -  person user1768759    schedule 08.11.2012
comment
@user1768759 user1768759 интересно - я не знал, что результат QSA не был активным, но в любом случае это упрощает простое копирование, потому что тогда любой зависимый код не должен заботиться о том, является ли список узлов активным или нет.   -  person Alnitak    schedule 08.11.2012
comment
@Alnitak спасибо, но не будет ли чище просто работать со списком узлов, а не преобразовывать его в массив? эта часть меня смущает, потому что каждая библиотека, на которую я смотрел сегодня, делает срез массива - и я подумал, что, возможно, упустил что-то важное там... просто не уверен, что? :-)   -  person user1768759    schedule 08.11.2012
comment
@Alnitak, ха-ха, о чем, черт возьми, я думаю?   -  person Ian    schedule 08.11.2012
comment
@user1768759 user1768759 Я не удивлюсь, если они будут использовать фрагмент массива, чтобы везде быть согласованными. Я почти уверен, что библиотеки пытаются использовать встроенные методы, такие как getElementById, querySelectorAll и т. д., поэтому библиотека точно не знает, какой список будет возвращен. Это их способ убедиться, что вы получаете статический список элементов, которые возвращаются из любого базового метода в этот конкретный момент времени и всегда будут доступны (если вы не манипулируете списком позже).   -  person Ian    schedule 08.11.2012
comment
@ Ян, я думаю, это то, что я только что сказал ;-)   -  person Alnitak    schedule 08.11.2012
comment
@Alnitak Мне просто нужно перестать говорить...   -  person Ian    schedule 08.11.2012
comment
Спасибо за обсуждение, ребята, последовательность, безусловно, имеет смысл. Я изучаю это для своей собственной минимальной библиотеки, ориентированной на мобильные устройства, поэтому на самом деле хотел удалить вещи, которые библиотеки помещают туда, чтобы обеспечить обратную совместимость. И, возможно, избыточный материал - я до сих пор не уверен, стоит ли хранить срез массива для общих вещей - в конце концов, это дополнительный вызов/операция, и, по сути, он создает потенциально большой массив элементов, который требует дополнительной памяти. .. не знаю, нужно будет еще немного почитать :)   -  person user1768759    schedule 08.11.2012


Ответы (2)


Обычно вам не нужно слайс/всплывать в списке узлов DOM.

На самом деле, да, и именно поэтому это необходимо.

Например, как еще будут работать .eq(), .first() и т.д. Объект jQuery содержит копию NodeList в массиве, а затем отдельные методы получают фрагменты этого массива. Точно так же вы не можете .add() узлов NodeList.

Кроме того, эти массивы должны оставаться действительными, даже если содержащиеся в них элементы впоследствии удаляются из DOM. Ссылки в массиве по-прежнему действительны, и их можно использовать для повторной вставки этих элементов обратно в DOM.

Если бы все, что у вас было, было бы живым NodeList, элементы автоматически исчезли бы из списка, когда они удаляются из DOM, и были бы потеряны навсегда, если бы у вас не было отдельной ссылки на них.

person Alnitak    schedule 08.11.2012
comment
@TinyGiant некоторые NodeList объекты живые, некоторые статичны. Тот, который возвращает querySelectorAll, является статическим. Предлагаем вам прочитать существенную ветку комментариев по вопросу выше. - person Alnitak; 08.09.2015
comment
Обратите внимание, что Array.prototype.slice.call(nodeList) не работает в некоторых версиях IE, если nodeList является элементом SVG. - person Stranded Kid; 30.09.2015

... потому что NodeList указывает на живые объекты DOM...

Нет, это недоразумение. Суть не в том, что каждый из узлов в списке является "живым" DOM-объектом (правда, но не имеет значения), а в том, что сам список является "живым" . Другими словами, содержимое списка может измениться (или даже увеличиться или уменьшиться) в любое время при изменении DOM.

Если вы выполняете операции над элементами в списке, которые изменяют DOM, сам список может изменяться на лету, пока вы его используете. Это означает, что под прикрытием ваш код может столкнуться с действительно странными проблемами рекурсии и/или производительности, даже если вы этого не осознаете и практически без каких-либо указаний в исходном коде.

Преобразование NodeList в обычный массив Javascript убирает эту «живость» самого списка и, таким образом, исключает возможность выстрелить себе в ногу.

person Chuck Kollars    schedule 19.11.2013
comment
@TinyGiant Некоторые, конечно, есть, например. aNode.childNodes. В вашем примере используется querySelectorAll, который возвращает неактивный NodeList. - person 1983; 08.09.2015