Explication de [] .slice.call en javascript?

197

Je suis tombé sur ce raccourci soigné pour convertir un DOM NodeList en un tableau régulier, mais je dois admettre que je ne comprends pas complètement comment cela fonctionne:

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

Cela commence donc par un tableau vide [], puisslice est utilisé pour convertir le résultat de calldans un nouveau tableau, oui?

Ce que je ne comprends pas, c'est le call. Comment cela se transforme-t-ildocument.querySelectorAll('a') d'un NodeList en un tableau normal?

Yansky
la source
5
Array.prototype.slice.call(document.querySelectorAll('a'));est une bonne façon d'écrire le morceau de code que vous avez écrit.
vdegenne
4
BTW, la méthode ES6 moderne (et intuitivement compréhensible) pour le même est Array.from. Par exemple, cela ferait la même chose: Array.from (document.querySelectorAll ('a'));
rugk

Réponses:

158

Ce qui se passe ici, c'est que vous appelez slice()comme si c'était une fonction de l' NodeListutilisation call(). Dans slice()ce cas, il s'agit de créer un tableau vide, puis de parcourir l'item sur NodeListlequel il s'exécute (à l'origine un tableau, maintenant a ) et de continuer à ajouter les éléments de cet objet au tableau vide qu'il a créé, qui est finalement renvoyé. Voici un article à ce sujet .

ÉDITER:

Donc, cela commence par un tableau vide [], puis une tranche est utilisée pour convertir le résultat de l'appel en un nouveau tableau, oui?

Ce n'est pas juste. [].slicerenvoie un objet fonction. Un objet fonction a une fonction call()qui appelle la fonction affectant le premier paramètre du call()à this; en d'autres termes, faire penser à la fonction qu'elle est appelée à partir du paramètre (le NodeListretourné par document.querySelectorAll('a')) plutôt qu'à partir d'un tableau.

Max Shawabkeh
la source
59
Notez également ici que bien que cela soit sémantiquement équivalent à dire Array.prototype.slice.call(...), il instancie en fait un objet tableau ( []) uniquement pour accéder à sa méthode de tranche de prototype. C'est une instanciation gaspillée. Dire à la Array.prototype.slice.call(...)place est plus propre, bien que vous ajoutiez plusieurs caractères à votre JS si vous comptez ...
Ben Zotto
Notez que cela ne fonctionne dans IE 8 et ci-dessous que sur les objets Array, vous ne pourrez donc pas cloner des NodeLists
Livingston Samuel
5
@quixoto []est plus fiable car il Arraypourrait être remplacé par autre chose. Si vous devez réutiliser Array#slice, c'est une bonne idée de le mettre en cache.
Mathias Bynens
2
Si quelqu'un d'autre cherche un moyen de le faire dans IE8, consultez cette question stackoverflow.com/questions/3199588/…
Liam Newmarch
1
En fait, j'ai vu ce modèle apparaître dans le code source de backbone.js: le fait var array = []; var push = array.push; var slice = array.slice; var splice = array.splice;-il pour le problème de sécurité mentionné par @MathiasBynens?
owensmartin
125

En JavaScript, les méthodes d'un objet peuvent être liées à un autre objet lors de l'exécution. En bref, javascript permet à un objet "d'emprunter" la méthode d'un autre objet:

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'

Les méthodes callet les applyobjets fonction (en JavaScript, les fonctions sont également des objets) vous permettent de le faire. Ainsi, dans votre code, vous pourriez dire que la NodeList emprunte la méthode de tranche d'un tableau. .slice()renvoie un autre tableau comme résultat, qui deviendra le tableau "converti" que vous pourrez ensuite utiliser.

Slebetman
la source
Explication du concept abstrait de Bang on for pour les fonctions de l'objet javascript. Maintenant, vous pouvez l' appliquer pour callfonction Array.prototypealias [].prototypevous.
Sourabh
29

Il récupère la slicefonction d'un Array. Il appelle ensuite cette fonction, mais en utilisant le résultat de document.querySelectorAllcomme thisobjet au lieu d'un tableau réel.

Brian Campbell
la source
19

C'est une technique pour convertir des objets de type tableau en tableaux réels.

Certains de ces objets incluent:

  • arguments dans les fonctions
  • NodeList (rappelez-vous que leur contenu peut changer après avoir été récupéré! Donc les convertir en tableau est un moyen de les figer)
  • Collections jQuery, alias objets jQuery (certains doc: API , type , learn )

Cela sert à de nombreuses fins, par exemple les objets sont passés par référence tandis que les tableaux sont passés par valeur.

Notez également que le premier argument 0peut être omis, explication approfondie ici .

Et pour être complet, il y a aussi jQuery.makeArray () .

Gras Double
la source
15

Comment cela se convertit-il document.querySelectorAll('a')d'un NodeList à un tableau normal?

Ceci est le code que nous avons,

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

Permet de le démonter en premier,

  []    // 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'

Étape: 1 Exécution de la callfonction

  • À l'intérieur call, à part le thisArg, le reste des arguments sera ajouté à une liste d'arguments.
  • Maintenant, la fonction slicesera invoquée en liant sa thisvaleur en tant que thisArg(comme un objet de type tableau provenait document.querySelector) et avec la liste d'arguments. ie] argument startqui contient0

Étape: 2 Exécution de la slicefonction invoquée à l'intérieur decall

  • startsera affecté à une variable scomme0
  • depuis endest undefined, this.lengthseront stockés danse
  • un tableau vide sera stocké dans une variable a
  • Après avoir effectué les réglages ci-dessus, l'itération suivante se produira

    while(s < e) {
      a.push(this[s]);
      s++;
    }
  • le tableau rempli asera retourné comme résultat.

PS Pour une meilleure compréhension de notre scénario, certaines étapes nécessaires à notre contexte ont été ignorées de l'algorithme original d' appel et de tranche .

Rajaprabhu Aravindasamy
la source
1
Très belle explication étape par étape. Impressionnant! Merci :)
kittu
1
Belle explication.
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
la source
4

Depuis ES6: créez simplement un tableau avec Array.from (element.children) ou Array.from ({length: 5})

Мони
la source