javascript - elemento - ¿Cómo funciona Array.prototype.slice.call()?
replace array javascript (13)
Sé que se usa para hacer que los argumentos sean una matriz real, pero no entiendo qué sucede cuando uso Array.prototype.slice.call(arguments)
Array.prototype.slice.call (argumentos) es la manera anticuada de convertir un argumento en una matriz.
En ECMAScript 2015, puede usar Array.from o el operador de propagación:
let args = Array.from(arguments);
let args = [...arguments];
El objeto de arguments
no es realmente una instancia de un Array y no tiene ninguno de los métodos de Array. Por lo tanto, los arguments.slice(...)
no funcionarán porque el objeto de argumentos no tiene el método de división.
Las matrices tienen este método, y debido a que el objeto de arguments
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é usar Array.prototype
? El Array
es el objeto desde el cual creamos nuevos arreglos ( new Array()
), y estos nuevos arreglos pasan métodos y propiedades, como slice. Estos métodos se almacenan en el objeto [Class].prototype
. Entonces, por razones de eficiencia, en lugar de acceder al método slice por (new Array()).slice.call()
o [].slice.call()
, 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. La razón por la que usamos slice, sin embargo, es más un "hack" que cualquier otra cosa. El método de división tomará, usted lo adivinó, división de una matriz y devolverá esa división como una nueva matriz. Al no pasarle ningún argumento (además del objeto de argumentos como su contexto), el método de división toma una parte completa de la "matriz" pasada (en este caso, el objeto de argumentos) y la devuelve como una nueva matriz.
Es porque, como señala MDN
El objeto de argumentos no es una matriz. Es similar a una matriz, pero no tiene propiedades de matriz excepto la longitud. Por ejemplo, no tiene el método pop. Sin embargo, se puede convertir a una matriz real:
Aquí estamos invocando el slice
en la Array
objetos nativos y no en su implementación y por eso el .prototype
adicional .prototype
var args = Array.prototype.slice.call(arguments);
Lo que sucede debajo del capó es que cuando .slice()
se llama normalmente, this
es un Array, y luego simplemente itera sobre ese Array, y hace su trabajo.
¿Cómo es this
en la función .slice()
una matriz? Porque cuando lo haces:
object.method();
... el object
se convierte automáticamente en el valor de this
en el method()
. Así que con:
[1,2,3].slice()
... la matriz [1,2,3]
se establece como el valor de this
en .slice()
.
Pero, ¿qué pasaría si pudieras sustituir otra cosa como el valor de this
? Siempre que lo que sustituyas tenga una propiedad numérica .length
y un conjunto de propiedades que sean índices numéricos, debería funcionar. Este tipo de objeto a menudo se llama un objeto similar a una matriz .
Los .call()
y .apply()
permiten establecer manualmente el valor de this
en una función. Entonces, si establecemos el valor de this
en .slice()
en un objeto similar a una matriz , .slice()
simplemente asumirá que está funcionando con una matriz, y hará su trabajo.
Tomemos este objeto simple como ejemplo.
var my_object = {
''0'': ''zero'',
''1'': ''one'',
''2'': ''two'',
''3'': ''three'',
''4'': ''four'',
length: 5
};
Obviamente, esto no es un Array, pero si puede configurarlo como this
valor de .slice()
, entonces solo funcionará, porque se parece a un Array para que .slice()
funcione correctamente.
var sliced = Array.prototype.slice.call( my_object, 3 );
Ejemplo: http://jsfiddle.net/wSvkv/
Como puede ver en la consola, el resultado es lo que esperamos:
[''three'',''four''];
Entonces, esto es lo que sucede cuando establece un objeto de arguments
como el valor de este .slice()
. Debido a que los arguments
tienen una propiedad .length
y un grupo de índices numéricos, .slice()
simplemente realiza su trabajo como si estuviera trabajando en una matriz real.
No se olvide, que los conceptos básicos de bajo nivel de este comportamiento es la conversión de tipos que se integra completamente en el motor JS.
Slice solo toma el objeto (gracias a la propiedadargent.length existente) y devuelve el objeto de matriz fundido después de realizar todas las operaciones en eso.
Las mismas lógicas que puede probar si intenta tratar el método de cadena con un valor INT:
String.prototype.bold.call(11); // returns "<b>11</b>"
Y eso explica la afirmación anterior.
Normalmente, llamando
var b = a.slice();
Copiará la matriz a
en b
. Sin embargo, no podemos hacer
var a = arguments.slice();
porque los arguments
no es una matriz real, y no tiene sector como método. Array.prototype.slice
es la función de división para matrices, y call
ejecuta la función con this
conjunto en arguments
.
Primero, debes leer cómo funciona la invocación de funciones en JavaScript . Sospecho que solo es suficiente para responder a tu pregunta. Pero aquí hay un resumen de lo que está sucediendo:
Array.prototype.slice
extrae el method slice
del prototype de Array
. Pero llamarlo directamente no funcionará, ya que es un método (no una función) y, por lo tanto, requiere un contexto (un objeto que llama, this
), de lo contrario, arrojaría Uncaught TypeError: Array.prototype.slice called on null or undefined
.
El método call()
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 método de someObject
exista en la cadena de prototipo de someObject
(como lo hace para Array
), mientras que el segundo permite que el contexto ( someObject
) se pase al método de forma manual.
Además, este último es corto para:
var slice = Array.prototype.slice;
slice.call(someObject, 1, 2);
Que es lo mismo que:
Array.prototype.slice.call(someObject, 1, 2);
Solo estoy escribiendo esto para recordarme ...
Array.prototype.slice.call(arguments);
== Array.prototype.slice(arguments[1], arguments[2], arguments[3], ...)
== [ arguments[1], arguments[2], arguments[3], ... ]
O simplemente use esta función práctica $ A para convertir la mayoría de las cosas en una matriz.
function hasArrayNature(a) {
return !!a && (typeof a == "object" || typeof a == "function") && "length" in a && !("setInterval" in a) && (Object.prototype.toString.call(a) === "[object Array]" || "callee" in a || "item" in a);
}
function $A(b) {
if (!hasArrayNature(b)) return [ b ];
if (b.item) {
var a = b.length, c = new Array(a);
while (a--) c[a] = b[a];
return c;
}
return Array.prototype.slice.call(b);
}
ejemplo de uso ...
function test() {
$A( arguments ).forEach( function(arg) {
console.log("Argument: " + arg);
});
}
Supongamos que tiene: function.apply(thisArg, argArray )
El método de aplicación invoca una función, pasando el objeto que se vinculará a esta y una matriz opcional de argumentos.
El método slice () selecciona una parte de una matriz y devuelve la nueva matriz.
Entonces, cuando llama a Array.prototype.slice.apply(arguments, [0])
, se invoca el método de división de matriz (vinculación) en los argumentos.
Tal vez un poco tarde, pero la respuesta a todo este lío es que call () se usa en JS para herencia. Si comparamos esto con Python o PHP, por ejemplo, la llamada se usa respectivamente como super (). init () o parent :: _ construct ().
Este es un ejemplo de su uso que aclara todo:
function Teacher(first, last, age, gender, interests, subject) {
Person.call(this, first, last, age, gender, interests);
this.subject = subject;
}
Referencia: https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Inheritance
Utiliza el método de división que tienen las matrices y lo llama, siendo this
el objeto de arguments
. Esto significa que lo llama como si hiciera arguments.slice()
asumiendo que los arguments
tenían tal método.
Crear una división sin ningún argumento simplemente tomará todos los elementos, así que simplemente copia los elementos de los arguments
a una matriz.
cuando .slice () se llama normalmente, esto es un Array, y luego simplemente itera sobre ese Array, y hace su trabajo.
//ARGUMENTS
function func(){
console.log(arguments);//[1, 2, 3, 4]
//var arrArguments = arguments.slice();//Uncaught TypeError: undefined is not a function
var arrArguments = [].slice.call(arguments);//cp array with explicity THIS
arrArguments.push(''new'');
console.log(arrArguments)
}
func(1,2,3,4)//[1, 2, 3, 4, "new"]
// 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]