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á this
a .slice()
função uma matriz? Porque quando você faz:
object.method();
... object
automaticamente se torna o valor de this
em method()
. Então com:
[1,2,3].slice()
... a [1,2,3]
matriz é definida como o valor de this
in .slice()
.
Mas e se você pudesse substituir outra coisa como this
valor? Desde que o que você substitua tenha uma .length
propriedade 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 this
em uma função. Então, se nós definir o valor this
no .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 this
valor 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 arguments
objeto como o this
valor de .slice()
. Como arguments
possui uma .length
propriedade e vários índices numéricos, .slice()
o trabalho é realizado como se estivesse trabalhando em uma matriz real.
Array.prototype.slice
descrição do método.
for-in
afirmação que não garante a ordem. O algoritmo usado por .slice()
define uma ordem numérica começando 0
e terminando (não inclusivo) com o .length
objeto 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-in
para 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 for
loop. Isso é o que .slice()
faz. Não importa se ele tem uma matriz real. Apenas inicia em 0
e acessa propriedades em um loop ascendente.
O arguments
objeto 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 arguments
objeto é 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].prototype
objeto. 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 a
para b
. No entanto, não podemos fazer
var a = arguments.slice();
porque arguments
não é uma matriz real e não tem slice
como método. Array.prototype.slice
é a slice
função para matrizes e call
executa a função com this
definido como arguments
.
prototype
? não é slice
um Array
mé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 Array
instância, mas não a Array
função construtora. Você usa prototype
para 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.slice
extrai 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
Array
this
Uncaught 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 slice
método exista na someObject
cadeia 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 slice
o objeto nativo Array
e 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 slice
método que as matrizes têm e o chama this
sendo o arguments
objeto. Isso significa que ele é chamado como se você arguments.slice()
assumisse que arguments
tinha esse método.
Criar uma fatia sem argumentos simplesmente usará todos os elementos - portanto, basta copiar os elementos arguments
para 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);
});
}