Что делает [].инструкция foreach.звоните() в JavaScript?


Я смотрел на некоторые фрагменты кода, и я нашел несколько элементов, вызывающих функцию над списком узлов с forEach, примененным к пустому массиву.

например, у меня есть что-то вроде:

[].forEach.call( document.querySelectorAll('a'), function(el) {
   // whatever with the current node
});

но я не могу понять, как это работает. Может ли кто-нибудь объяснить мне поведение пустого массива перед forEach и как call работает?

9 98

9 ответов:

[] - это массив.
Этот массив не используется вообще.

он помещается на страницу, потому что использование массива дает вам доступ к прототипам массива, например .forEach.

это просто быстрее, чем набирать текст Array.prototype.forEach.call(...);

далее forEach - это функция, которая принимает функцию в качестве входа...

[1,2,3].forEach(function (num) { console.log(num); });

...и для каждого элемента this (где this массив-как, в том, что он имеет length и вы можете получить доступ к его части как this[1]) пройдет три вещи:

  1. элемент в массиве
  2. индекс элемента (третий элемент будет проходить 2)
  3. ссылка на массив

и наконец, .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 метод возвращает a NodeList, который похож на массив, но это не совсем массив. Поэтому у него нет 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 возвращает a NodeList это не имеет метода 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);
        }
    };
}