В чем разница между использованием [] .forEach.call () и просто добавлением элемента в массив к родительскому узлу в JavaScript? [Дубликат]

Указатель NULL - это тот, который указывает на никуда. Когда вы разыскиваете указатель p, вы говорите «дайте мне данные в месте, хранящемся в« p ». Когда p является нулевым указателем, местоположение, хранящееся в p, является nowhere, вы говорите «Дайте мне данные в месте« нигде ». Очевидно, он не может этого сделать, поэтому он выбрасывает NULL pointer exception.

В общем, это потому, что что-то не было правильно инициализировано.

100
задан Manolis 15 October 2014 в 14:35
поделиться

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"

Поэтому вы создаете быстрый способ вызова функции forEach, и вы меняете this из пустого массива на список всех тегов <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) и Beyond

Не только slice( ) / array( ) / и т. д., чтобы облегчить жизнь людям, которые хотят использовать списки, так же, как они используют массивы (как и должны), но для людей, у которых есть роскошь работы в браузерах ES6 + относительно близких будущее или «трансбинирование» в Вавилоне сегодня, у вас есть встроенные языковые функции, которые делают ненужным этот тип вещей.

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 ];

Супер-чистый и очень полезный. Найдите операторы Rest и Spread ; попробуйте их на сайте BabelJS; если ваш технический стек в порядке, используйте их в производстве с Babel и шаг сборки.


Нет никакой веской причины, чтобы не использовать преобразование из не-массива в массив ... ... просто не делайте беспорядок своего кода, ничего не делая , но вставляя ту же уродливую линию, везде.

150
ответ дан Norguard 20 August 2018 в 13:15
поделиться
  • 1
    Теперь я немного смущен, если я это сделал: console.log (this); Я всегда получаю объект окна. И я думал, что .call изменяет значение этого – Muhammad Saleh 17 April 2015 в 16:03
  • 2
    .call выполняет изменение значения this, поэтому вы используете call с функцией forEach. Если forEach выглядит как forEach (fn) { var i = 0; var len = this.length; for (; i < length; i += 1) { fn( this[i], i, this ); }, то, изменяя this, говоря forEach.call( newArrLike ), означает, что он будет использовать этот новый объект как this. – Norguard 17 April 2015 в 16:14
  • 3
    Да, но если бы я сделал console.log(this) внутри функции forEach, я должен получить nodeList, добавленный мной через .call, потому что я изменил значение этого, однако меня смущает то, что this равен окну, а не nodeList – Muhammad Saleh 19 April 2015 в 08:12
  • 4
    @MuhammadSaleh .call не изменяет значение this внутри того, что вы переходите на forEach, .call изменяет значение this внутри forEach . Если вы смущены тем, что this не передается из forEach в функцию, которую вы хотели вызвать, вы должны искать поведение this в JavaScript. В качестве дополнения, применимого только здесь (и map / filter / reduce), есть второй аргумент forEach, который устанавливает this внутри функции arr.forEach(fn, thisInFn) или [].forEach.call(arrLike, fn, thisInFn); или просто использует .bind; arr.forEach(fn.bind(thisInFn)); – Norguard 19 April 2015 в 12:03
  • 5
    @thetrystero это точно. [] существует как массив, так что вы можете .call метод .forEach и изменить this на набор, над которым хотите работать. .call выглядит так: function (thisArg, a, b, c) { var method = this; method(a, b, c); }, кроме внутренней черной магии, чтобы установить this внутри method равным тому, что вы передали как thisArg. – Norguard 23 November 2015 в 20:47

Просто быстрое и грязное решение, которое я всегда использую. Я не стал бы трогать прототипы, как хорошую практику. Конечно, есть много способов сделать это лучше, но вы получите эту идею.

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}`);
});
1
ответ дан Alfredo Maria Milano 20 August 2018 в 13:15
поделиться

Его можно лучше написать, используя

Array.prototype.forEach.call( document.querySelectorAll('a'), function(el) {

});

. Что такое is document.querySelectorAll('a') возвращает объект, похожий на массив, но он не наследуется от типа Array. Поэтому мы вызываем метод forEach из объекта Array.prototype с контекстом как значением, возвращаемым document.querySelectorAll('a')

4
ответ дан Arun P Johny 20 August 2018 в 13:15
поделиться

Просто добавьте одну строку:

NodeList.prototype.forEach = HTMLCollection.prototype.forEach = Array.prototype.forEach;

И voila!

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

Наслаждайтесь: -)

Warning: NodeList - это глобальный класс. Не используйте эту рекомендацию, если вы пишете публичную библиотеку. Однако это очень удобный способ повысить самоэффективность при работе на веб-сайте или приложении node.js.

1
ответ дан imos 20 August 2018 в 13:15
поделиться

Метод querySelectorAll возвращает NodeList, который похож на массив, но это не совсем массив. Таким образом, у него нет метода forEach (какие объекты массива наследуются через Array.prototype).

Так как NodeList похож на массив, методы массива будут работать на нем, поэтому используя [].forEach.call, вы вызываете метод Array.prototype.forEach в контексте NodeList, как если бы вы могли просто сделать yourNodeList.forEach(/*...*/).

Обратите внимание, что пустой литерал массива просто ярлык к расширенной версии, который вы, вероятно, увидите довольно часто:

Array.prototype.forEach.call(/*...*/);
40
ответ дан James Allardice 20 August 2018 в 13:15
поделиться
  • 1
    Итак, чтобы прояснить, нет никакого преимущества в использовании [].forEach.call(['a','b'], cb) над ['a','b'].forEach(cb) в повседневных приложениях со стандартными массивами, просто при попытке итерации по структурам, подобным массивам, которые не имеют forEach на собственном прототипе? Это верно? – Matt Fletcher 30 January 2015 в 13:16
  • 2
    @MattFletcher: Да, это правильно. Оба будут работать, но почему слишком сложно? Просто вызовите метод непосредственно в самом массиве. – James Allardice 30 January 2015 в 13:21
  • 3
    Хорошо, спасибо. И я не знаю, почему, может быть, просто для уличного кредитора, впечатляющего старушек в ALDI и тому подобного. Я буду придерживаться [].forEach() :) – Matt Fletcher 30 January 2015 в 13:23
  • 4

Другие ответы очень хорошо объяснили этот код, поэтому я просто добавлю предложение.

Это хороший пример кода, который нужно реорганизовать для простоты и ясности. Вместо использования [].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
});
14
ответ дан Michael Geary 20 August 2018 в 13:15
поделиться

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);
        }
    };
}
0
ответ дан Mikel 20 August 2018 в 13:15
поделиться

Пустой массив имеет свойство forEach в своем прототипе, который является объектом Function. (Пустой массив - это простой способ получить ссылку на функцию forEach, которую имеют все объекты Array.) Объекты функций, в свою очередь, имеют свойство call, которое также является функцией. Когда вы вызываете функцию call функции, она запускает функцию с заданными аргументами. Первый аргумент становится this в вызываемой функции.

Здесь вы можете найти документацию для функции call здесь . Документация для forEach здесь здесь .

1
ответ дан Ted Hopp 20 August 2018 в 13:15
поделиться

[] всегда возвращает новый массив, он эквивалентен new Array(), но гарантированно возвращает массив, потому что Array может быть перезаписан пользователем, тогда как [] не может. Таким образом, это безопасный способ получить прототип Array, а затем, как описано, call используется для выполнения функции в узле arraylike (this).

Вызывает функцию с данное значение и аргументы предоставляются отдельно. mdn

0
ответ дан Xotic750 20 August 2018 в 13:15
поделиться
  • 1
    В то время как [] на самом деле всегда возвращает массив, а window.Array может быть перезаписан каким-либо (во всех старых браузерах), так что window.Array.prototype.forEach может быть перезаписано чем угодно. В обоих случаях нет гарантий безопасности, в браузерах, которые не поддерживают геттеры / сеттеры или герметизацию / замораживание объекта (замораживание, вызывающее проблемы его размножения). – Norguard 19 April 2015 в 14:31
  • 2
    Не стесняйтесь редактировать ответ, чтобы добавить дополнительную информацию о возможности перезаписи prototype или его методов: forEach. – Xotic750 19 April 2015 в 20:29
Другие вопросы по тегам:

Похожие вопросы: