Wyjaśnienie [] .slice.call w javascript?

197

Natknąłem się na ten zgrabny skrót do konwersji DOM NodeList w zwykłą tablicę, ale muszę przyznać, że nie do końca rozumiem, jak to działa:

[].slice.call(document.querySelectorAll('a'), 0)

Więc zaczyna się od pustej tablicy [], a następnie slicesłuży do konwersji wyniku callna nową tablicę, tak?

Nie rozumiem tylko tego call. Jak to się przekształca document.querySelectorAll('a')z NodeList do zwykłej tablicy?

Yansky
źródło
5
Array.prototype.slice.call(document.querySelectorAll('a'));to właściwy sposób na napisanie fragmentu kodu, który napisałeś.
vdegenne,
4
BTW, nowoczesna (i intuicyjnie zrozumiała) metoda ES6 dla tego samego jest Array.from. Tak więc np. Zrobiłoby to samo: Array.from (document.querySelectorAll ('a'));
rugk

Odpowiedzi:

158

To, co się tutaj dzieje, polega na tym, że dzwonisz slice()tak, jakby to była funkcja NodeListużywania call(). Co slice()robi w tym przypadku jest utworzyć pustą tablicę, następnie iterację obiektu to działa (pierwotnie na tablicy, teraz NodeList) i utrzymać dołączanie elementów tego obiektu do pustej tablicy utworzony, który ostatecznie powrócił. Oto artykuł na ten temat .

EDYTOWAĆ:

Zaczyna się więc od pustej tablicy [], a następnie kawałek służy do konwersji wyniku wywołania na nową tablicę, tak?

To nie tak. [].slicezwraca obiekt funkcji. Obiekt funkcji ma funkcję, call()która wywołuje funkcję przypisującą pierwszy parametr call()do this; innymi słowy, sprawiając, że funkcja myśli, że jest wywoływana z parametru ( NodeListzwróconego przez document.querySelectorAll('a')), a nie z tablicy.

Max Shawabkeh
źródło
59
Zauważ też, że chociaż jest to semantycznie równoważne z powiedzeniem Array.prototype.slice.call(...), w rzeczywistości tworzy on obiekt tablicowy ( []) tylko w celu uzyskania dostępu do jego metody wycinania prototypów. To zmarnowana instancja. Mówienie Array.prototype.slice.call(...)zamiast tego jest czystsze, chociaż dodajesz kilka znaków do JS, jeśli liczysz ...
Ben Zotto
Zauważ, że działa to w IE 8 i niższych tylko na obiektach Array, więc nie będziesz w stanie klonować NodeLists
Livingston Samuel
5
@quixoto []jest bardziej niezawodny, ponieważ Arraymożna go zastąpić czymś innym. Jeśli musisz użyć ponownie Array#slice, dobrze jest buforować.
Mathias Bynens
2
Jeśli ktoś inny szuka sposobu, aby to zrobić w IE8, sprawdź to pytanie stackoverflow.com/questions/3199588/...
Liam Newmarch,
1
W rzeczywistości widziałem ten wzorzec pojawiający się w kodzie źródłowym backbone.js: var array = []; var push = array.push; var slice = array.slice; var splice = array.splice;Czy robi to w związku z kwestią bezpieczeństwa wspomnianą przez @MathiasBynens?
owensmartin
125

W JavaScript metody obiektu można powiązać z innym obiektem w czasie wykonywania. W skrócie, javascript pozwala obiektowi „pożyczyć” metodę innego obiektu:

object1 = {
    name: 'Frank',
    greet() {
        alert(`Hello ${this.name}`);
    }
};

object2 = {
    name: 'Andy'
};

// Note that object2 has no greet method,
// but we may "borrow" from object1:

object1.greet.call(object2); // Will show an alert with 'Hello Andy'

Te calli applymetody obiektów funkcyjnych (w JavaScript, funkcje są obiektami, jak również) pozwala to zrobić. Zatem w kodzie można powiedzieć, że NodeList pożycza metodę plasterka tablicy. .slice()zwraca inną tablicę jako wynik, która stanie się „skonwertowaną” tablicą, z której będziesz mógł korzystać.

Slebetman
źródło
Bang on 🎯 abstrakcyjne wyjaśnienie koncepcji funkcji obiektu javascript. Teraz możesz zastosować go do callfunkcji Array.prototypeaka [].prototypesiebie.
Sourabh
29

Pobiera slicefunkcję z Array. Następnie wywołuje tę funkcję, ale używając wyniku document.querySelectorAlljako thisobiektu zamiast rzeczywistej tablicy.

Brian Campbell
źródło
19

Jest to technika przekształcania obiektów tablicowych w prawdziwe tablice.

Niektóre z tych obiektów obejmują:

  • arguments w funkcjach
  • NodeList (pamiętaj, że ich zawartość może się zmienić po pobraniu! Więc konwersja ich do tablicy jest sposobem na ich zamrożenie)
  • Kolekcje jQuery, czyli obiekty jQuery (niektóre dokumenty: API , typ , nauka )

Służy to wielu celom, na przykład obiekty są przekazywane przez odniesienie, a tablice są przekazywane przez wartość.

Zauważ też, że pierwszy argument 0można pominąć, dokładne wyjaśnienie tutaj .

Dla zapewnienia kompletności istnieje również jQuery.makeArray () .

Gras Double
źródło
15

Jak to się przekształca document.querySelectorAll('a')z NodeList tablicy na zwykłą?

To jest kod, który mamy,

[].slice.call(document.querySelectorAll('a'), 0)

Najpierw go zdemontujmy,

  []    // Array object
.slice // Accessing the function 'slice' present in the prototype of Array
.call // Accessing the function 'call' present in the prototype of function object(slice)
(document.querySelectorAll('a'),0) 
    // 'call' can have arguments like, (thisArg, arg1,arg2...n). 
   // So here we are passing the 'thisArg' as an array like object,
  // that is a 'nodeList'. It will be served as 'this' object inside of slice function.
 // And finally setting 'start' argument of slice as '0' and leaving the 'end' 
// argument as 'undefined'

Krok: 1 Wykonanie callfunkcji

  • Wewnątrz call, poza tym thisArg, pozostałe argumenty zostaną dołączone do listy argumentów.
  • Teraz funkcja slicezostanie wywołana przez powiązanie jej thiswartości jako thisArg(pochodzi z obiektu typu tablicowego document.querySelector) i listy argumentów. ie] argument, startktóry zawiera0

Krok: 2 Wywołanie slicefunkcji wywołanej wewnątrzcall

  • startzostanie przypisany do zmiennej sjako0
  • ponieważ endjest undefined, this.lengthbędzie przechowywany we
  • pusta tablica będzie przechowywana w zmiennej a
  • Po wprowadzeniu powyższych ustawień nastąpi następująca iteracja

    while(s < e) {
      a.push(this[s]);
      s++;
    }
  • ajako wynik zostanie zwrócona wypełniona tablica .

PS Aby lepiej zrozumieć nasz scenariusz, niektóre kroki niezbędne dla naszego kontekstu zostały zignorowane z oryginalnego algorytmu call and slice .

Rajaprabhu Aravindasamy
źródło
1
Bardzo ładne wyjaśnienie krok po kroku. Niesamowite! Dziękuję :)
Kittu
1
Niezłe wyjaśnienie.
NaveenDA
7
[].slice.call(document.querySelectorAll('.slide'));

1. The querySelectorAll() method returns all elements in the document that matches a specified selector(s). 

2. The call() method calls a function with a given this value and arguments provided individually.

3. The slice() method returns the selected elements in an array, as a new array object.

  so this line return the array of [object HTMLDivElement]. Here is the six div with classname "slide" so array length will be 6.

<div class="slideshow">

  <div class="slide">
    first slider1
  </div>
  <div class="slide">
    first slider2
  </div>
  <div class="slide">
    first slider3
  </div>
  <div class="slide">
    first slider4
  </div>
  <div class="slide">
    first slider5
  </div>
  <div class="slide">
    first slider6
  </div>

</div>

<script type="text/javascript">

  var arraylist = [].slice.call(document.querySelectorAll('.slide'));

  alert(arraylist);

</script>
Ankit Parmar
źródło
4

Z ES6: Po prostu stwórz tablicę za pomocą Array.from (element.children) lub Array.from ({length: 5})

Мони
źródło