Co robi [].forEach.call () do w JavaScript?

Przeglądałem fragmenty kodu i znalazłem wiele elementów wywołujących funkcję nad listą węzłów z foreachem zastosowanym do pustej tablicy.

Na przykład mam coś takiego:

[].forEach.call( document.querySelectorAll('a'), function(el) {
   // whatever with the current node
});
Ale nie rozumiem, jak to działa. Czy ktoś może mi wyjaśnić zachowanie pustej tablicy przed forEach i jak działa call?
Author: Manolis, 2013-04-17

9 answers

[] to tablica.
Ta tablica w ogóle nie jest używana.

Jest umieszczana na stronie, ponieważ użycie tablicy daje dostęp do prototypów tablicy, takich jak .forEach.

To jest po prostu szybsze niż pisanie Array.prototype.forEach.call(...);

Następnie, {[8] } jest funkcją, która przyjmuje funkcję jako wejście...

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

...i dla każdego elementu w this (gdzie {[9] } jest podobne do tablicy, ponieważ ma length i możesz uzyskać dostęp do jego części, takich jak this[1]) przejdzie trzy rzeczy:

  1. element w tablicy
  2. indeks elementu (trzeci element przejdzie 2)
  3. odniesienie do tablicy

Wreszcie, .call jest prototypem, który ma Funkcje (Jest to funkcja, która jest wywoływana na innych funkcjach).
.call pobierze swój pierwszy argument i zastąpi this wewnątrz zwykłej funkcji tym, co przekazałeś call, ponieważ pierwszy argument (undefined lub null użyje window w codziennym JS, lub będzie cokolwiek zdałeś, jeśli w "trybie ścisłym"). Reszta argumentów zostanie przekazana do oryginalnej funkcji.

[1, 2, 3].forEach.call(["a", "b", "c"], function (item, i, arr) {
    console.log(i + ": " + item);
});
// 0: "a"
// 1: "b"
// 2: "c"

Dlatego tworzysz szybki sposób wywołania funkcji forEach i zmieniasz this z pustej tablicy na listę wszystkich znaczników <a>, a dla każdego <a> w kolejności, wywołujesz podaną funkcję.

Edytuj

Logiczne Wnioski / Oczyszczanie

Poniżej znajduje się link do artykułu sugerującego, że programowanie funkcjonalne, i trzymać się ręcznego, inline pętli, za każdym razem, ponieważ to rozwiązanie jest hack-owski i nieestetyczny.

Powiedziałbym, że chociaż .forEach jest mniej pomocny niż jego odpowiedniki, .map(transformer), .filter(predicate), .reduce(combiner, initialValue), nadal służy celom, gdy wszystko, co naprawdę chcesz zrobić, to zmodyfikować świat zewnętrzny (nie tablicę), n-razy, mając dostęp do arr[i] lub i.

Więc jak sobie radzić z dysproporcją, ponieważ Motto jest wyraźnie utalentowany i kompetentny facet, a ja chciałbym sobie wyobrazić, że wiem,co robię / gdzie idę (teraz i wtedy... ...innym razem jest to nauka head-first)?

Odpowiedź jest dość prosta i coś, co Wujek Bob i Sir Crockford będą facepalm, ze względu na niedopatrzenie:]}

clean it up.

function toArray (arrLike) { // or asArray(), or array(), or *whatever*
  return [].slice.call(arrLike);
}

var checked = toArray(checkboxes).filter(isChecked);
checked.forEach(listValues);
Jeśli kwestionujesz, czy musisz to zrobić, odpowiedź może brzmieć "nie"...
Dokładnie to się robi... ...każdy(?) biblioteka z funkcje wyższego rzędu w dzisiejszych czasach.
Jeśli używasz lodash, podkreślenia lub nawet jQuery, wszystkie będą miały sposób na wykonanie zestawu elementów i wykonanie akcji n-razy.
Jeśli nie używasz czegoś takiego, to napisz swoją własną.
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);
};
[[59]}Aktualizacja dla ES6 (ES2015) i dalej

Nie tylko jest slice( )/array( )/metoda etc helper ułatwi życie ludziom, którzy chcą używać list tak jak używają tablic (tak jak powinni), ale dla ludzi dla tych, którzy mają luksus działania w przeglądarkach ES6+ stosunkowo niedalekiej przyszłości, lub "transpiling" w Babel dzisiaj, masz wbudowane funkcje językowe, które sprawiają, że tego typu rzeczy nie są potrzebne.

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

Super-czysty i bardzo przydatny.
Spójrz w górę odpoczynek oraz Spread operatorzy; wypróbuj je na stronie BabelJS; jeśli twój stos technologiczny jest w porządku, użyj ich w produkcji z Babel I etapem budowania.


There ' s no good powód, aby nie móc użyć transformacji z nie-tablicy do tablicy... ...po prostu nie rób bałaganu w swoim kodzie nie robiąc nic , ale wklejając ten sam brzydki wiersz, wszędzie.

 155
Author: Norguard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-19 21:30:55

The querySelectorAll Metoda zwraca NodeList, która jest podobna do tablicy, ale nie jest do końca tablicą. Dlatego nie posiada metody forEach (które obiekty tablicy dziedziczą poprzez Array.prototype).

Ponieważ NodeList jest podobna do tablicy, metody tablicy będą na niej działać, więc używając [].forEach.call wywołujesz metodę Array.prototype.forEach w kontekście NodeList, tak jakbyś był w stanie po prostu zrobić yourNodeList.forEach(/*...*/).

Zauważ, że pusta tablica jest tylko skrótem do rozwiniętego wersja, którą prawdopodobnie zobaczysz też dość często:

Array.prototype.forEach.call(/*...*/);
 40
Author: James Allardice,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-17 06:52:45

Inne odpowiedzi bardzo dobrze wyjaśniły ten kod, więc dodam tylko sugestię.

Jest to dobry przykład kodu, który powinien być refakturowany dla uproszczenia i przejrzystości. Zamiast używać [].forEach.call() lub Array.prototype.forEach.call() za każdym razem, gdy to robisz, zrób z tego prostą funkcję:

function forEach( list, callback ) {
    Array.prototype.forEach.call( list, callback );
}

Teraz możesz wywołać tę funkcję zamiast bardziej skomplikowanego i niejasnego kodu:

forEach( document.querySelectorAll('a'), function( el ) {
   // whatever with the current node
});
 15
Author: Michael Geary,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-17 07:00:33

Można lepiej napisać używając

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

});

To, co robi, to document.querySelectorAll('a') zwraca obiekt podobny do tablicy, ale nie dziedziczy z typu Array. Wywołujemy więc metodę forEach z obiektu Array.prototype z kontekstem jako wartością zwracaną przez document.querySelectorAll('a')

 4
Author: Arun P Johny,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-17 07:02:49

Pusta tablica ma właściwość forEach w swoim prototypie, która jest obiektem funkcji. (Pusta tablica jest po prostu łatwym sposobem uzyskania odniesienia do funkcji forEach, którą posiadają wszystkie obiekty Array.) Obiekty funkcyjne z kolei mają właściwość call, która jest również funkcją. Gdy wywołujesz funkcję call, uruchamia ona funkcję z podanymi argumentami. Pierwszy argument staje się this w wywołanej funkcji.

Możesz znaleźć dokumentację dla funkcji call tutaj . Dokumentacja dla forEach jest tutaj .

 1
Author: Ted Hopp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-17 06:51:34

Wystarczy dodać jedną linijkę:

NodeList.prototype.forEach = HTMLCollection.prototype.forEach = Array.prototype.forEach;
I voila!
document.querySelectorAll('a').forEach(function(el) {
  // whatever with the current node
});

Enjoy: -)

Uwaga: NodeList jest klasą globalną. Nie używaj tego polecenia, jeśli piszesz Bibliotekę Publiczną. Jest to jednak bardzo wygodny sposób na zwiększenie własnej skuteczności podczas pracy na stronie internetowej lub węźle.aplikacja js.

 1
Author: imos,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-24 13:17:28

Tylko szybkie i brudne rozwiązanie, które zawsze używam. Nie tknąłbym prototypów, tak samo jak dobry trening. Oczywiście, istnieje wiele sposobów, aby to poprawić, ale masz pomysł.

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
Author: Alfredo Maria Milano,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-02 05:59:44

[] zawsze zwraca nową tablicę, jest ona równoważna new Array(), ale jest gwarantowana, że zwróci tablicę, ponieważ Array może zostać nadpisana przez użytkownika, podczas gdy [] nie może. Jest to więc bezpieczny sposób na uzyskanie prototypu Array, a następnie, zgodnie z opisem, {[5] } jest używany do wykonania funkcji na arraylike nodelist (this).

Wywołuje funkcję o podanej tej wartości i podanych argumentach indywidualnie mdn

 0
Author: Xotic750,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-17 07:10:33

Norguard wyjaśnił co [].forEach.call() does and James Allardice Dlaczego robimy to: ponieważ querySelectorAll zwraca NodeList, która nie ma metody forEach...

Chyba że masz nowoczesną przeglądarkę jak Chrome 51+, Firefox 50+, Opera 38, Safari 10.

Jeśli nie możesz dodać Polifill:

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
Author: Mikel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-27 10:51:21