Explicação de [] .slice.call em javascript?

197

Eu me deparei com este atalho puro para converter um DOM NodeList em uma matriz regular, mas devo admitir que não entendo completamente como ele funciona:

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

Então começa com uma matriz vazia [], depois sliceé usada para converter o resultado de calluma nova matriz, sim?

O pouco que eu não entendo é o call. Como isso converte document.querySelectorAll('a')de um NodeList para uma matriz regular?

Yansky
fonte
5
Array.prototype.slice.call(document.querySelectorAll('a'));é uma maneira adequada de escrever o pedaço de código que você escreveu.
precisa saber é o seguinte
4
BTW, o método ES6 moderno (e intuitivamente compreensível) para o mesmo é Array.from. Então, por exemplo, isso faria o mesmo: Array.from (document.querySelectorAll ('a'));
rugk

Respostas:

158

O que está acontecendo aqui é que você chama slice()como se fosse uma função do NodeListuso call(). O que slice()faz neste caso é criar uma matriz vazia, então iterate através do objeto que está sendo executado (originalmente um array, agora um NodeList) e mantê acrescentando os elementos desse objeto para a matriz vazia que criou, que é finalmente voltou. Aqui está um artigo sobre isso .

EDITAR:

Então começa com uma matriz vazia [], então a fatia é usada para converter o resultado da chamada em uma nova matriz, sim?

Isso não está certo. [].sliceretorna um objeto de função. Um objeto de função possui uma função call()que chama a função que atribui o primeiro parâmetro do call()para this; em outras palavras, fazer com que a função pense que está sendo chamada do parâmetro ( NodeListretornado por document.querySelectorAll('a')) e não de uma matriz.

Max Shawabkeh
fonte
59
Observe também aqui que, embora isso seja semanticamente equivalente a dizer Array.prototype.slice.call(...), ele na verdade instancia um objeto de matriz ( []) apenas para acessar seu método de fatia de protótipo. Essa é uma instanciação perdida. Dizer, ao Array.prototype.slice.call(...)contrário, é mais limpo, embora você adicione vários caracteres ao seu JS se estiver contando ...
Ben Zotto
Observe que isso funciona no IE 8 e abaixo apenas em objetos Array, portanto você não poderá clonar NodeLists
Livingston Samuel
5
O @quixoto []é mais confiável, pois Arraypode ser substituído por outra coisa. Se você precisar reutilizar Array#slice, é uma boa ideia armazená-lo em cache.
Mathias Bynens 22/03
2
No caso de alguém mais está procurando uma maneira de fazer isso no IE8, confira esta questão stackoverflow.com/questions/3199588/...
Liam Newmarch
1
Na verdade, eu vi esse padrão aparecer no código fonte do backbone.js: var array = []; var push = array.push; var slice = array.slice; var splice = array.splice;ele faz isso pela questão de segurança que @MathiasBynens menciona?
Owensmartin
125

Em JavaScript, os métodos de um objeto podem ser vinculados a outro objeto em tempo de execução. Em resumo, o javascript permite que um objeto "empreste" o método de outro 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'

Os métodos calle applydos objetos de função (em JavaScript, as funções também são objetos) permitem fazer isso. Portanto, no seu código, você poderia dizer que o NodeList está emprestando o método de fatia de uma matriz. .slice()retorna outra matriz como resultado, que se tornará a matriz "convertida" que você poderá usar.

slebetman
fonte
Bang on explanation explicação do conceito abstrato para as funções do objeto javascript. Agora, você pode aplicá-lo para a callfunção de Array.prototypeaka [].prototypevocê mesmo.
Sourabh 24/02
29

Ele recupera a slicefunção de um Array. Em seguida, chama essa função, mas usando o resultado de document.querySelectorAllcomo o thisobjeto em vez de uma matriz real.

Brian Campbell
fonte
19

É uma técnica para converter objetos do tipo matriz em matrizes reais.

Alguns desses objetos incluem:

  • arguments em funções
  • NodeList (lembre-se de que o conteúdo pode mudar após serem buscados! Portanto, convertê-los em array é uma maneira de congelá-los)
  • coleções jQuery, também conhecidas como objetos jQuery (alguns documentos: API , tipo , aprender )

Isso serve para muitos propósitos, por exemplo, objetos são passados ​​por referência, enquanto matrizes são passadas por valor.

Além disso, observe que o primeiro argumento 0pode ser omitido, explicação completa aqui .

E por uma questão de integridade, também há jQuery.makeArray () .

Gras Double
fonte
15

Como isso converte document.querySelectorAll('a')de uma NodeList matriz para uma matriz regular?

Este é o código que temos,

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

Vamos desmontá-lo primeiro,

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

Etapa: 1 Execução da callfunção

  • Dentro call, além do thisArg, o restante dos argumentos será anexado a uma lista de argumentos.
  • Agora a função sliceserá invocada vinculando seu thisvalor como thisArg(o objeto semelhante ao array veio document.querySelector) e com a lista de argumentos. ie] argumento startque contém0

Etapa: 2 Execução da slicefunção chamada dentro docall

  • startserá atribuído a uma variável scomo0
  • desde que endseja undefined, this.lengthserá armazenado eme
  • uma matriz vazia será armazenada em uma variável a
  • Depois de fazer as configurações acima, a seguinte iteração ocorrerá

    while(s < e) {
      a.push(this[s]);
      s++;
    }
  • a matriz preenchida aserá retornada como resultado.

PS Para uma melhor compreensão do nosso cenário, algumas etapas necessárias para o nosso contexto foram ignoradas no algoritmo original de chamada e fatia .

Rajaprabhu Aravindasamy
fonte
1
Muito boa explicação passo a passo. Impressionante! Obrigado :)
kittu
1
Boa explicação.
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
fonte
4

No ES6: Simplesmente crie um array com Array.from (element.children) ou Array.from ({length: 5})

Мони
fonte