Что делает [].инструкция foreach.call () делать в 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
, as первый аргумент (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>
в порядке, вы вызываете функцию предоставлена.
редактировать
Логическое Заключение / Очистка
ниже есть ссылка на статью, предлагающую нам отказаться от попыток функционального программирования и придерживаться ручного, встроенного цикла каждый раз, потому что это решение Хак-иш и неприглядно.
я бы сказал, что во время .forEach
менее полезно, чем его коллеги, .map(transformer)
, .filter(predicate)
, .reduce(combiner, initialValue)
, он по-прежнему служит целям, когда все, что вы действительно хотите сделать, это изменить снаружи мир (не массив), n-раз, имея доступ либо к arr[i]
или i
.
Итак, как мы справляемся с неравенством, поскольку девиз явно талантливый и знающий парень, и я хотел бы представить, что я знаю, что я делаю/куда я иду (время от времени... ...в других случаях это head-first learning)?
ответ на самом деле довольно прост, и что-то дядя Боб и Сэр Крокфорд будут оба 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 и шагом сборки.
нет веской причины не использовать преобразование из не-массива в массив... ...просто не порть свой код, ничего не делая. но вставить ту же уродливую линию, везде.
на querySelectorAll
метод возвращает NodeList
, который похож на массив, но это не совсем массив. Поэтому у него нет forEach
метод (какие объекты массива наследуются через Array.prototype
).
С 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
function, он запускает функцию с заданными аргументами. Первым аргументом становится this
в вызываемой функции.
вы можете найти документацию call
функции здесь. Документация forEach
is здесь.
просто добавьте одну строку:
NodeList.prototype.forEach = HTMLCollection.prototype.forEach = Array.prototype.forEach;
и вуаля!
document.querySelectorAll('a').forEach(function(el) {
// whatever with the current node
});
наслаждайтесь :-)
предупреждение: NodeList является глобальным классом. Не используйте эту рекомендацию, если вы пишете публичную библиотеку. Однако это очень удобный способ повышения самоэффективности при работе на веб-сайте или узле.приложение на JS.
просто быстрое и грязное решение, которое я всегда использую. Я бы не стал трогать прототипы, просто хорошая практика. Конечно, есть много способов сделать это лучше, но вы понимаете идею.
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 возвращает NodeList
у этого нет метода forEach...
Если у вас нет современного браузера, такого как Chrome 51+, Firefox 50+, Opera 38, Safari 10.
Если нет, вы можете добавить Polyfill:
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);
}
};
}