¿Cómo funciona `Array.prototype.slice.call`?

Resuelto ilyo asked hace 13 años • 15 respuestas

Sé que se usa para hacer argumentsun real Array, pero no entiendo qué pasa al usarlo Array.prototype.slice.call(arguments);.

ilyo avatar Aug 14 '11 19:08 ilyo
Aceptado

Lo que sucede bajo el capó es que cuando .slice()se llama normalmente, thises una matriz, y luego simplemente itera sobre esa matriz y hace su trabajo.

¿Cómo está thisen la .slice()función un Array? Porque cuando lo haces:

object.method();

... el objectautomáticamente se convierte en el valor de thisen el method(). Entonces con:

[1,2,3].slice()

...la [1,2,3]matriz se establece como el valor de thisin .slice().


Pero, ¿y si pudieras sustituir el thisvalor por otra cosa? Siempre que lo que sustituya tenga una .lengthpropiedad numérica y un montón de propiedades que sean índices numéricos, debería funcionar. Este tipo de objeto a menudo se denomina objeto similar a una matriz .

Los métodos .call()y .apply()le permiten establecer manualmente el valor de thisen una función. Entonces , si establecemos el valor de thisin .slice()en un objeto similar a una matriz , .slice()asumiremos que está funcionando con una matriz y hará lo suyo.

Tome este objeto simple como ejemplo.

var my_object = {
    '0': 'zero',
    '1': 'one',
    '2': 'two',
    '3': 'three',
    '4': 'four',
    length: 5
};

Obviamente, esto no es una matriz, pero si puede establecerlo como el thisvalor de .slice(), entonces simplemente funcionará, porque se parece lo suficiente a una matriz para .slice()funcionar correctamente.

var sliced = Array.prototype.slice.call( my_object, 3 );

Ejemplo: http://jsfiddle.net/wSvkv/

Como puedes ver en la consola, el resultado es el que esperábamos:

['three','four'];

Entonces esto es lo que sucede cuando estableces un argumentsobjeto como el thisvalor de .slice(). Debido a que argumentstiene una .lengthpropiedad y un montón de índices numéricos, .slice()simplemente realiza su trabajo como si estuviera trabajando en una matriz real.

user113716 avatar Aug 14 '2011 13:08 user113716

El argumentsobjeto no es en realidad una instancia de un Array y no tiene ninguno de los métodos del Array. Por lo tanto, arguments.slice(...)no funcionará porque el objeto de argumentos no tiene el método de corte.

Las matrices tienen este método y, debido a que el argumentsobjeto es muy similar a una matriz, los dos son compatibles. Esto significa que podemos usar métodos de matriz con el objeto de argumentos. Y dado que los métodos de matriz se crearon teniendo en cuenta las matrices, devolverán matrices en lugar de otros objetos de argumento.

Entonces, ¿por qué utilizar Array.prototype? Es Arrayel objeto a partir del cual creamos nuevas matrices ( new Array()), y a estas nuevas matrices se les pasan métodos y propiedades, como segmento. Estos métodos se almacenan en el [Class].prototypeobjeto. Entonces, por motivos de eficiencia, en lugar de acceder al método de corte mediante (new Array()).slice.call()o [].slice.call(), simplemente lo obtenemos directamente del prototipo. Esto es para que no tengamos que inicializar una nueva matriz.

Pero, ¿por qué tenemos que hacer esto en primer lugar? Bueno, como dijiste, convierte un objeto de argumentos en una instancia de Array. Sin embargo, la razón por la que usamos slice es más un "truco" que otra cosa. El método de corte tomará, como habrá adivinado, un segmento de una matriz y devolverá ese segmento como una nueva matriz. No pasarle ningún argumento (aparte del objeto de argumentos como contexto) hace que el método de corte tome una parte completa de la "matriz" pasada (en este caso, el objeto de argumentos) y la devuelva como una nueva matriz.

Ben avatar Aug 14 '2011 13:08 Ben

Normalmente llamando

var b = a.slice();

Copiará la matriz aen b. Sin embargo, no podemos hacer

var a = arguments.slice();

porque argumentsno tiene slicecomo método (no es una matriz real).

Array.prototype.slicees la slicefunción para matrices. .callejecuta esta slicefunción, con el thisvalor establecido en arguments.

Delan Azabani avatar Aug 14 '2011 13:08 Delan Azabani

Array.prototype.slice.call(arguments) es la forma antigua de convertir argumentos en una matriz.

En ECMAScript 2015, puede utilizar Array.from o el operador de extensión:

let args = Array.from(arguments);

let args = [...arguments];
Alexander Moiseyev avatar Jul 28 '2017 10:07 Alexander Moiseyev

Primero, deberías leer cómo funciona la invocación de funciones en JavaScript . Sospecho que eso por sí solo es suficiente para responder a tu pregunta. Pero he aquí un resumen de lo que está pasando:

Array.prototype.sliceextrae el método del prototipo . Pero llamarlo directamente no funcionará, ya que es un método (no una función) y por lo tanto requiere un contexto (un objeto de llamada ), de lo contrario arrojaría .slice ArraythisUncaught TypeError: Array.prototype.slice called on null or undefined

El call()método le permite especificar el contexto de un método, básicamente haciendo que estas dos llamadas sean equivalentes:

someObject.slice(1, 2);
slice.call(someObject, 1, 2);

Excepto que el primero requiere que el slicemétodo exista en someObjectla cadena de prototipos (como ocurre con Array), mientras que el segundo permite que el contexto ( someObject) se pase manualmente al método.

Además, este último es la abreviatura de:

var slice = Array.prototype.slice;
slice.call(someObject, 1, 2);

Que es lo mismo que:

Array.prototype.slice.call(someObject, 1, 2);
Marco Roy avatar Dec 19 '2015 00:12 Marco Roy
// We can apply `slice` from  `Array.prototype`:
Array.prototype.slice.call([]); //-> []

// Since `slice` is available on an array's prototype chain,
'slice' in []; //-> true
[].slice === Array.prototype.slice; //-> true

// … we can just invoke it directly:
[].slice(); //-> []

// `arguments` has no `slice` method
'slice' in arguments; //-> false

// … but we can apply it the same way:
Array.prototype.slice.call(arguments); //-> […]

// In fact, though `slice` belongs to `Array.prototype`,
// it can operate on any array-like object:
Array.prototype.slice.call({0: 1, length: 1}); //-> [1]
sam avatar Aug 20 '2013 00:08 sam