Eu sei que é usado para transformar argumentos em uma matriz real, mas não entendo o que acontece ao usar Array.prototype.slice.call(arguments)
Eu sei que é usado para transformar argumentos em uma matriz real, mas não entendo o que acontece ao usar Array.prototype.slice.call(arguments)
Respostas:
O que acontece sob o capô é que, quando .slice()é chamado normalmente, thisé uma matriz e, em seguida, itera sobre essa matriz e faz seu trabalho.
Como está thisa .slice()função uma matriz? Porque quando você faz:
object.method();
... objectautomaticamente se torna o valor de thisem method(). Então com:
[1,2,3].slice()
... a [1,2,3]matriz é definida como o valor de thisin .slice().
Mas e se você pudesse substituir outra coisa como thisvalor? Desde que o que você substitua tenha uma .lengthpropriedade numérica e várias propriedades que são índices numéricos, ele deve funcionar. Esse tipo de objeto geralmente é chamado de objeto de matriz .
Os métodos .call()e .apply()permitem definir manualmente o valor de thisem uma função. Então, se nós definir o valor thisno .slice()a um array como objeto , .slice()só vai assumir que está trabalhando com uma matriz, e vai fazer a sua coisa.
Tome esse objeto simples como exemplo.
var my_object = {
'0': 'zero',
'1': 'one',
'2': 'two',
'3': 'three',
'4': 'four',
length: 5
};
Obviamente, isso não é uma matriz, mas se você pode defini-la como o thisvalor de .slice(), funcionará apenas porque parece suficiente com uma matriz para .slice()funcionar corretamente.
var sliced = Array.prototype.slice.call( my_object, 3 );
Exemplo: http://jsfiddle.net/wSvkv/
Como você pode ver no console, o resultado é o que esperamos:
['three','four'];
Então é isso que acontece quando você define um argumentsobjeto como o thisvalor de .slice(). Como argumentspossui uma .lengthpropriedade e vários índices numéricos, .slice()o trabalho é realizado como se estivesse trabalhando em uma matriz real.
Array.prototype.slicedescrição do método.
for-inafirmação que não garante a ordem. O algoritmo usado por .slice()define uma ordem numérica começando 0e terminando (não inclusivo) com o .lengthobjeto especificado (ou Matriz ou o que for). Portanto, é garantido que o pedido seja consistente em todas as implementações.
var obj = {2:"two", 0:"zero", 1: "one"}. Se usarmos for-inpara enumerar o objeto, não há garantia de ordem. Mas se usarmos for, podemos aplicar manualmente a ordem: for (var i = 0; i < 3; i++) { console.log(obj[i]); }. Agora sabemos que as propriedades do objeto serão alcançadas na ordem numérica crescente que definimos pelo nosso forloop. Isso é o que .slice()faz. Não importa se ele tem uma matriz real. Apenas inicia em 0e acessa propriedades em um loop ascendente.
O argumentsobjeto não é realmente uma instância de uma matriz e não possui nenhum dos métodos de matriz. Portanto, arguments.slice(...)não funcionará porque o objeto de argumentos não possui o método de fatia.
As matrizes têm esse método e, como o argumentsobjeto é muito semelhante a uma matriz, as duas são compatíveis. Isso significa que podemos usar métodos de matriz com o objeto de argumentos. E como os métodos de matriz foram criados com matrizes em mente, eles retornarão matrizes em vez de outros objetos de argumento.
Então, por que usar Array.prototype? O Arrayé o objeto do qual criamos novas matrizes a partir de ( new Array()), e essas novas matrizes passam métodos e propriedades, como fatia. Esses métodos são armazenados no [Class].prototypeobjeto. Portanto, por questões de eficiência, em vez de acessar o método de fatia por (new Array()).slice.call()ou [].slice.call(), apenas obtemos isso diretamente do protótipo. Isso é para que não tenhamos que inicializar uma nova matriz.
Mas por que temos que fazer isso em primeiro lugar? Bem, como você disse, ele converte um objeto de argumentos em uma instância Array. A razão pela qual usamos fatia, no entanto, é mais um "hack" do que qualquer coisa. O método de fatia terá uma fatia de uma matriz, você adivinhou, e retornará essa fatia como uma nova matriz. Não passar argumentos para ele (além do objeto de argumentos como seu contexto) faz com que o método de fatia pegue uma parte completa da "matriz" passada (nesse caso, o objeto de argumentos) e a retorne como uma nova matriz.
Normalmente, chamando
var b = a.slice();
copiará a matriz apara b. No entanto, não podemos fazer
var a = arguments.slice();
porque argumentsnão é uma matriz real e não tem slicecomo método. Array.prototype.sliceé a slicefunção para matrizes e callexecuta a função com thisdefinido como arguments.
prototype? não é sliceum Arraymétodo nativo ?
Arrayé uma função construtora e a "classe" correspondente é Array.prototype. Você também pode usar[].slice
sliceé um método de cada Arrayinstância, mas não a Arrayfunção construtora. Você usa prototypepara acessar métodos das instâncias teóricas de um construtor.
Primeiro, você deve ler como a chamada de função funciona em JavaScript . Eu suspeito que só isso é suficiente para responder à sua pergunta. Mas aqui está um resumo do que está acontecendo:
Array.prototype.sliceextrai o método a partir do protótipo . Mas chamá-lo diretamente não funcionará, pois é um método (não uma função) e, portanto, requer um contexto (um objeto que chama ), caso contrário, seria possível .slice ArraythisUncaught TypeError: Array.prototype.slice called on null or undefined
O call()método permite especificar o contexto de um método, basicamente tornando essas duas chamadas equivalentes:
someObject.slice(1, 2);
slice.call(someObject, 1, 2);
Exceto que o primeiro requer que o slicemétodo exista na someObjectcadeia de protótipos do (como faz para Array), enquanto o último permite que o contexto ( someObject) seja passado manualmente para o método.
Além disso, o último é a abreviação de:
var slice = Array.prototype.slice;
slice.call(someObject, 1, 2);
Qual é o mesmo que:
Array.prototype.slice.call(someObject, 1, 2);
// 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]
Array.prototype.slice.call (argumentos) é a maneira antiquada de converter argumentos em uma matriz.
No ECMAScript 2015, você pode usar Array.from ou o operador de spread:
let args = Array.from(arguments);
let args = [...arguments];
É porque, como observa o MDN
O objeto de argumentos não é uma matriz. É semelhante a uma matriz, mas não possui nenhuma propriedade da matriz, exceto o comprimento. Por exemplo, ele não possui o método pop. No entanto, pode ser convertido em uma matriz real:
Aqui estamos chamando sliceo objeto nativo Arraye não sua implementação e é por isso que o extra.prototype
var args = Array.prototype.slice.call(arguments);
Não se esqueça, que um básico de baixo nível desse comportamento é a conversão de tipo que foi totalmente integrada ao mecanismo JS.
A fatia apenas pega o objeto (graças à propriedade argument.length existente) e retorna o objeto de matriz convertido após fazer todas as operações nele.
A mesma lógica que você pode testar se tentar tratar o método String com um valor INT:
String.prototype.bold.call(11); // returns "<b>11</b>"
E isso explica a afirmação acima.
Ele usa o slicemétodo que as matrizes têm e o chama thissendo o argumentsobjeto. Isso significa que ele é chamado como se você arguments.slice()assumisse que argumentstinha esse método.
Criar uma fatia sem argumentos simplesmente usará todos os elementos - portanto, basta copiar os elementos argumentspara uma matriz.
Vamos supor que você tenha: function.apply(thisArg, argArray )
O método apply chama uma função, transmitindo o objeto que será vinculado a isso e a uma matriz opcional de argumentos.
O método slice () seleciona uma parte de uma matriz e retorna a nova matriz.
Portanto, quando você chama Array.prototype.slice.apply(arguments, [0])o método de fatia da matriz, é invocado (bind) nos argumentos.
Talvez um pouco tarde, mas a resposta para toda essa bagunça é que call () é usado em JS para herança. Se compararmos isso com Python ou PHP, por exemplo, call é usado respectivamente como super (). init () ou parent :: _ construct ().
Este é um exemplo de seu uso que esclarece tudo:
function Teacher(first, last, age, gender, interests, subject) {
Person.call(this, first, last, age, gender, interests);
this.subject = subject;
}
Referência: https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Inheritance
quando .slice () é chamado normalmente, isso é uma matriz e, em seguida, itera sobre essa matriz e faz seu trabalho.
//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"]
Só estou escrevendo isso para me lembrar ...
Array.prototype.slice.call(arguments);
== Array.prototype.slice(arguments[1], arguments[2], arguments[3], ...)
== [ arguments[1], arguments[2], arguments[3], ... ]
Ou apenas use esta útil função $ A para transformar a maioria das coisas em uma 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);
}
exemplo de uso ...
function test() {
$A( arguments ).forEach( function(arg) {
console.log("Argument: " + arg);
});
}