Explicación de [] .slice.call en javascript?

197

Me topé con este atajo ordenado para convertir un DOM NodeList en una matriz regular, pero debo admitir que no entiendo completamente cómo funciona:

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

Entonces comienza con una matriz vacía [], luego slicese usa para convertir el resultado callen una nueva matriz, ¿sí?

Lo poco que no entiendo es el call. ¿Cómo se convierte eso document.querySelectorAll('a')de una NodeList a una matriz regular?

Yansky
fuente
55
Array.prototype.slice.call(document.querySelectorAll('a'));es una forma adecuada de escribir el fragmento de código que escribió.
vdegenne
44
Por cierto, el método ES6 moderno (e intuitivamente comprensible) para el mismo es Array.from. Entonces, por ejemplo, esto haría lo mismo: Array.from (document.querySelectorAll ('a'));
Rugk

Respuestas:

158

Lo que sucede aquí es que llamas slice()como si fuera una función del NodeListuso call(). Lo que slice()hace en este caso es crear una matriz vacía, luego iterar a través del objeto en el que se está ejecutando (originalmente una matriz, ahora a NodeList) y seguir agregando los elementos de ese objeto a la matriz vacía que creó, que finalmente se devuelve. Aquí hay un artículo sobre esto .

EDITAR:

Entonces comienza con una matriz vacía [], luego el segmento se usa para convertir el resultado de la llamada a una nueva matriz, ¿sí?

Eso no está bien. [].slicedevuelve un objeto de función. Un objeto de función tiene una función call()que llama a la función que asigna el primer parámetro de call()to this; en otras palabras, hacer que la función piense que se llama desde el parámetro ( NodeListdevuelto por document.querySelectorAll('a')) en lugar de desde una matriz.

Max Shawabkeh
fuente
59
Tenga en cuenta también que, aunque esto es semánticamente equivalente a decir Array.prototype.slice.call(...), en realidad crea una instancia de un objeto de matriz ( []) solo para acceder a su método de corte prototipo. Esa es una instanciación desperdiciada. Decir que en Array.prototype.slice.call(...)cambio es más limpio, aunque agregue varios caracteres a su JS si está contando ...
Ben Zotto
Tenga en cuenta que esto funciona en IE 8 y versiones posteriores solo en objetos de matriz, por lo que no podrá clonar NodeLists
Livingston Samuel
55
@quixoto []es más confiable ya que Arraypodría sobrescribirse a otra cosa. Si necesita reutilizar Array#slice, es una buena idea guardarlo en caché.
Mathias Bynens
2
En caso de que alguien más esté buscando una manera de hacer esto en IE8, consulte esta pregunta stackoverflow.com/questions/3199588/…
Liam Newmarch
1
De hecho, vi este patrón aparecer en el código fuente de backbone.js: var array = []; var push = array.push; var slice = array.slice; var splice = array.splice;¿Hace esto por el problema de seguridad que menciona @MathiasBynens?
owensmartin
125

En JavaScript, los métodos de un objeto pueden vincularse a otro objeto en tiempo de ejecución. En resumen, javascript permite que un objeto "tome prestado" el método de otro objeto:

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'

Los métodos cally applyde los objetos de función (en JavaScript, las funciones también son objetos) le permiten hacer esto. Entonces, en su código, podría decir que NodeList está tomando prestado el método de división de una matriz. .slice()devuelve otra matriz como resultado, que se convertirá en la matriz "convertida" que luego puede usar.

slebetman
fuente
Explosión en la explicación del concepto abstracto para las funciones del objeto javascript. Ahora, puede aplicarlo para la callfunción de Array.prototypeaka [].prototypeusted mismo.
Sourabh
29

Recupera la slicefunción de un Array. Luego llama a esa función, pero usa el resultado de document.querySelectorAllcomo el thisobjeto en lugar de una matriz real.

Brian Campbell
fuente
19

Es una técnica para convertir objetos tipo matriz en matrices reales.

Algunos de estos objetos incluyen:

  • arguments en funciones
  • NodeList (¡recuerde que su contenido puede cambiar después de ser recuperado! Por lo tanto, convertirlos en una matriz es una forma de congelarlos)
  • Colecciones jQuery, también conocidos como objetos jQuery (algunos documentos: API , tipo , aprendizaje )

Esto sirve para muchos propósitos, por ejemplo, los objetos se pasan por referencia, mientras que las matrices se pasan por valor.

Además, tenga en cuenta que el primer argumento 0se puede omitir, una explicación detallada aquí .

Y en aras de la integridad, también hay jQuery.makeArray () .

Gras Double
fuente
15

¿Cómo se convierte eso document.querySelectorAll('a')de una NodeList a una matriz regular?

Este es el código que tenemos,

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

Vamos a desmontarlo primero

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

Paso: 1 Ejecución de la callfunción

  • Dentro call, aparte del thisArg, el resto de los argumentos se agregarán a una lista de argumentos.
  • Ahora slicese invocará la función al vincular su thisvalor como thisArg(matriz de la que proviene el objeto document.querySelector) y con la lista de argumentos. es decir] argumento startque contiene0

Paso: 2 Ejecución de la slicefunción invocada dentro decall

  • startserá asignado a una variable scomo0
  • desde endes undefined, this.lengthserán almacenados ene
  • una matriz vacía se almacenará en una variable a
  • Después de realizar la configuración anterior, se realizará la siguiente iteración

    while(s < e) {
      a.push(this[s]);
      s++;
    }
  • la matriz llena ase devolverá como resultado.

PD Para una mejor comprensión de nuestro escenario, algunos pasos que son necesarios para nuestro contexto han sido ignorados del algoritmo original de llamada y corte .

Rajaprabhu Aravindasamy
fuente
1
Muy buena explicación paso a paso. ¡Increíble! Gracias :)
kittu
1
Buena explicación
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
fuente
4

Desde ES6: simplemente cree una matriz con Array.from (element.children) o Array.from ({length: 5})

Мони
fuente