Что делает [].инструкция foreach.звоните() в JavaScript?
Я смотрел на некоторые фрагменты кода, и я нашел несколько элементов, вызывающих функцию над списком узлов с forEach, примененным к пустому массиву.
например, у меня есть что-то вроде:
[].forEach.call( document.querySelectorAll('a'), function(el) {
// whatever with the current node
});
но я не могу понять, как это работает. Может ли кто-нибудь объяснить мне поведение пустого массива перед forEach и как call
работает?
9 ответов:
[]
- это массив.
Этот массив не используется вообще.он помещается на страницу, потому что использование массива дает вам доступ к прототипам массива, например
.forEach
.это просто быстрее, чем набирать текст
Array.prototype.forEach.call(...);
далее
forEach
- это функция, которая принимает функцию в качестве входа...[1,2,3].forEach(function (num) { console.log(num); });
...и для каждого элемента
this
(гдеthis
массив-как, в том, что он имеетlength
и вы можете получить доступ к его части какthis[1]
) пройдет три вещи:
- элемент в массиве
- индекс элемента (третий элемент будет проходить
2
)- ссылка на массив
и наконец,
.call
является прототипом, который функции имеют (это функция, которая вызывается на другие функции)..call
возьмет свой первый аргумент и заменитthis
внутри обычной функции с тем, что вы прошлиcall
, а первый аргумент (undefined
илиnull
использоватьwindow
в повседневной JS, или будет все, что вы прошли, если в "строгом режиме"). Остальные аргументы будут переданы в исходную функцию.[1, 2, 3].forEach.call(["a", "b", "c"], function (item, i, arr) { console.log(i + ": " + item); }); // 0: "a" // 1: "b" // 2: "c"
таким образом, вы создаете быстрый способ вызова , а ты
this
из пустого массива в список всех<a>
теги, и для каждого<a>
в порядке, вы вызываете функцию предоставлена.EDIT
Логический Вывод / Очистка
ниже есть ссылка на статью, предлагающую нам отказаться от попыток функционального программирования и придерживаться ручного, встроенного цикла, каждый раз, потому что это решение является взломанным и неприглядным.
я бы сказал, что во время
.forEach
менее полезно, чем его коллеги,.map(transformer)
,.filter(predicate)
,.reduce(combiner, initialValue)
, он по-прежнему служит целям, когда все, что вы действительно хотите сделать, это изменить внешний вид мире (не массив), N-раз, при этом имея доступ кarr[i]
илиi
.Итак, как мы справляемся с неравенством, поскольку девиз явно талантливый и знающий парень, и я хотел бы представить, что я знаю, что я делаю/куда я иду (время от времени... ...в других случаях это головное обучение)?
ответ на самом деле довольно прост, и что-то дядя Боб и Сэр Крокфорд будут оба facepalm, из-за надзор:
его убрать.
function toArray (arrLike) { // or asArray(), or array(), or *whatever* return [].slice.call(arrLike); } var checked = toArray(checkboxes).filter(isChecked); checked.forEach(listValues);
теперь, если вы спрашиваете, нужно ли вам это делать, то ответ вполне может быть нет...
Именно это и делается... ...каждый(?) библиотека с функциями более высокого порядка в эти дни.
Если вы используете lodash или подчеркивание или даже jQuery, все они будут иметь способ взять набор элементов и выполнить действие n раз.
Если вы не используете такой вещь, тогда во что бы то ни стало, пиши свою собственную.lib.array = (arrLike, start, end) => [].slice.call(arrLike, start, end); lib.extend = function (subject) { var others = lib.array(arguments, 1); return others.reduce(appendKeys, subject); };
обновление для ES6 (ES2015) и за его пределами
не только
slice( )
/array( )
/etc вспомогательный метод собирается сделать жизнь проще для людей, которые хотят использовать списки так же, как они используют массивы (как и должны), но для людей, которые имеют роскошь работать в браузерах ES6+ относительно близкого будущего или "транспилинга" в Babel сегодня, у вас есть встроенные языковые функции, которые делают этот тип вещей ненужный.function countArgs (...allArgs) { return allArgs.length; } function logArgs (...allArgs) { return allArgs.forEach(arg => console.log(arg)); } function extend (subject, ...others) { /* return ... */ } var nodeArray = [ ...nodeList1, ...nodeList2 ];
супер-чистый и очень полезный.
Посмотрите на остальное и распространение операторы; попробуйте их на сайте BabelJS; если ваш технический стек в порядке, используйте их в производстве с Babel и шагом сборки.
нет никаких веских причин, чтобы не иметь возможности использовать преобразование из не-массива в массив... ...просто не делайте беспорядок из вашего кода ничего не делая но наклеивание той же уродливой линии, везде.
The
querySelectorAll
метод возвращает aNodeList
, который похож на массив, но это не совсем массив. Поэтому у него нетforEach
метод (какие объекты массива наследуются черезArray.prototype
).С a
NodeList
похож на массив, методы массива будут фактически работать на нем, поэтому с помощью[].forEach.call
вы призываетеArray.prototype.forEach
метод в контекстеNodeList
, как будто вы были в состоянии просто сделатьyourNodeList.forEach(/*...*/)
.обратите внимание, что пустой массив literal - это просто ярлык для расширенной версии, который вы, вероятно, тоже увидите довольно часто:
Array.prototype.forEach.call(/*...*/);
другие ответы объяснили этот код очень хорошо, поэтому я просто добавлю предложение.
Это хороший пример кода, который должен быть приведен к простоте и ясности. Вместо того, чтобы использовать
[].forEach.call()
илиArray.prototype.forEach.call()
каждый раз, когда вы делаете это, сделайте из него простую функцию:function forEach( list, callback ) { Array.prototype.forEach.call( list, callback ); }
теперь вы можете вызвать эту функцию вместо более сложного и непонятного кода:
forEach( document.querySelectorAll('a'), function( el ) { // whatever with the current node });
это может быть лучше написано с помощью
Array.prototype.forEach.call( document.querySelectorAll('a'), function(el) { });
что это
document.querySelectorAll('a')
возвращает объект, похожий на массив, но он не наследуется отArray
тип. Поэтому мы называемforEach
методArray.prototype
объект с контекстом в качестве значения, возвращаемогоdocument.querySelectorAll('a')
пустой массив имеет свойство
forEach
в его прототипе, который является объектом функции. (Пустой массив-это простой способ получить ссылку на элемент вот и всеArray
объекты.) Функциональные объекты, в свою очередь, имеютcall
свойство, которое также является функцией. При вызове функцииcall
функция, она запускает функцию с заданными аргументами. Первый аргумент становитсяthis
в вызываемой функции.вы можете найти документацию
call
функции здесь. Документация дляforEach
и здесь.
просто добавьте одну строку:
NodeList.prototype.forEach = HTMLCollection.prototype.forEach = Array.prototype.forEach;
и вуаля!
document.querySelectorAll('a').forEach(function(el) { // whatever with the current node });
наслаждайтесь :-)
предупреждение: NodeList-это глобальный класс. Не используйте эту рекомендацию, если вы пишете публичную библиотеку. Однако это очень удобный способ для повышения самоэффективности при работе на сайте или узле.js app.
просто быстрое и грязное решение, которое я всегда использую. Я бы не стал трогать прототипы, просто как хорошая практика. Конечно, есть много способов сделать это лучше, но вы получите идею.
const forEach = (array, callback) => { if (!array || !array.length || !callback) return for (var i = 0; i < array.length; i++) { callback(array[i], i); } } forEach(document.querySelectorAll('.a-class'), (item, index) => { console.log(`Item: ${item}, index: ${index}`); });
[]
всегда возвращает новый массив, это эквивалентноnew Array()
но гарантированно возвращает массив, потому чтоArray
может быть перезаписан пользователем, тогда как[]
не может. Так что это безопасный способ получить прототипArray
, тогда как описано,call
используется для выполнения функции в arraylike nodelist (this).вызывает функцию с заданным значением и аргументами индивидуально. mdn
Norguard объяснил что
[].forEach.call()
и Джеймс Аллардиспочему мы делаем это: потому что querySelectorAll возвращает aNodeList
это не имеет метода forEach...Если у вас нет современного браузера, такого как Chrome 51+, Firefox 50+, Opera 38, Safari 10.
Если нет, вы можете добавить полифилл:
if (window.NodeList && !NodeList.prototype.forEach) { NodeList.prototype.forEach = function (callback, thisArg) { thisArg = thisArg || window; for (var i = 0; i < this.length; i++) { callback.call(thisArg, this[i], i, this); } }; }