ForEach() vs Array.prototype.forEach.call()
Я просто просматривал образцы кода демонов Electron API, когда внезапно появилось дикое выражение, совершенно чуждое мне:
const links = document.querySelectorAll('a[href]');
Array.prototype.forEach.call(links, function (link) {
// WWIII here
})
Я определенно понимаю, что делает этот кусок кода, но я привык к синтаксису:
links.forEach(function (links) {});
Так в чем же разница между этими двумя? Я уже читал различные темы StackOverflow по этой теме, но они либо неоднозначны, либо вообще не отвечают на вопрос. Некоторые сказали, что у него есть что-то, что связано с массивными коллекциями, которые не могут быть итерационными .forEach() в отличие от Array.prototype.forEach.call(). Это единственное преимущество слишком утомительной и длинной версии?
Спасибо заранее!
Ответы
Ответ 1
"методы класса" в JavaScript являются фактически функциями, определенными в prototype
. Это означает, что даже если объект не наследуется от прототипа Array
, вы можете вызвать на нем методы Array
, если это следует за структурой массива (т.е. это объект с свойством length
и свойствами, индексированными целыми числами). Однако объект не ссылается на Array.prototype
, поэтому вам нужно явно выбрать Array.prototype
как объект, в котором находится метод.
Функция document.querySelectorAll
возвращает NodeList
, который не является ни Array
и не наследуется от прототипа Array
. Однако, поскольку NodeList
имеет аналогичную внутреннюю структуру для Array
, вы все равно можете использовать функцию forEach
. Но поскольку NodeList
не наследуется от прототипа Array
, попытка использовать .forEach
на NodeList
вызовет ошибку (это не совсем так - см. Примечание в конце моего ответа). По этой причине вам нужно явно указать, что вы вызываете метод из Array.prototype
на NodeList
, и это делается с помощью .call
метод от Function.prototype
.
Вкратце:
Array.prototype.forEach.call(links, function(link) { /* something */ })
означает:
Возьмите функцию forEach
из Array.prototype
и назовите ее на links
, которая является объектом не Array
, а в качестве аргумента используется некоторая функция.
Обратите внимание, что в последних версиях браузеров прототип NodeList
предоставляет a forEach
метод, который работает так же, как Array
один, поэтому пример из API электрона, вероятно, использует версию Array
для совместимости со старыми версиями. Если у вас есть веб-приложение и вы заботитесь только о поддержке современных версий Chrome и Firefox, вы можете просто позвонить forEach
на ваш NodeList
. Фактически, поскольку Электрон обновляется примерно через 2 недели после каждого обновления Chrome, безопасно использовать NodeList.prototype.forEach
в Electron.:)
Ответ 2
Это может быть связано с тем, как document.querySelectorAll
возвращает статический NodeList
, а не a Array
.
Используя прототип Array
, вы все равно можете вызвать forEach
, он похож на действующий на аргументы.
Ответ 3
Это интересный вопрос. Полгода назад я бы сказал, что link.forEach
не о более коротком синтаксисе, но на самом деле он не должен работать. Затем я хотел бы объяснить, что это значит, что многие методы массива преднамеренно генерируют, а это означает, что их внутренняя реализация учитывает только числовые индексы и свойство length объекта this
, но не заботится о том, что это экземпляр массива. В основном, что сказал @Pedro Кастильо в своем ответе.
Однако теперь я скажу, что в эти дни вечнозеленые браузеры (за исключением IE11, Edge, по состоянию на апрель 2017 года) уже реализованы NodeList.prototype.forEach, поэтому вам больше не нужно использовать .call
hack или Array.from
, чтобы просто перебрать NodeList с помощью forEach
.
Итак, мое резюме: если вам не нужно поддерживать IE, используйте NodeList.prototype.forEach
, а не Array.prototype.forEach
. Это может быть одно и то же внутренне, но более чистое концептуально. Если вам нужно поддерживать IE, и вы не хотите включать еще один pollyfill, используйте Array.prototype.call
или лучше Array.from
.