Imagem mostra uma rua de uma cidade cheias de carros e com prédios altos de ambos os lados

Dicas de bolso de JavaScript - parte 2

Criação de um array

Alguém usa o construtor new Array()? Eu particularmente não uso, mas quando vi isso, achei legal deixar registrado pois era algo que não sabia e vai que no dia a dia encontramos algum projeto com isso.

Se criarmos um array através do new Array() passando argumentos entre os parênteses, temos um array com esses valores que foram passados. Algo assim:

var megaSena = new Array(20, 29, 32, 45, 55, 58);

Seria o mesmo caso fizéssemos isso:

var megaSena = [20, 29, 32, 45, 55, 58];

E se passarmos apenas um valor como argumento? Teríamos um array com apenas um item, certo? Mahomenos… Aí que vem a pegadinha do JavaScript malandro. Se criarmos usando a sintaxe literal, realmente temos um array com apenas um item:

var megaSena = [20];

console.log(megaSena.length); // 1
console.log(megaSena[0]); // 20

Agora se criarmos usando new Array() o que acontece é que esse único valor passado como argumento vira o comprimento do array e não um item dele.

var megaSena = new Array(20);

console.log(megaSena.length); // 20
console.log(megaSena[0]); // undefined

Funções imediatas e parâmetros

Já ouvimos bastante a respeito de funções imediatas, correto? Recapitulando rapidamente, ela permite que uma função seja executada assim que seja definida. Isso é bom principalmente pelo fato de fornecer um escopo temporário para a mágica que você vai fazer, sem a necessidade de poluir seu escopo global.

(function() {
  // some magic
}());

Uma coisa bacana é que podemos passar argumentos para as funções imediatas. Podemos então ter algo assim:

(function(name, hobby) {
  console.log('Hi, my name is ' + name + ' and I like ' + hobby );
}('Fabeni', 'to travel'));

// "Hi, my name is Fabeni and I like to travel"

Como observado pelo Mauricio Soares, uma grande vantagem de passar parâmetros para uma função imediata (IIFE), é que esse valor é passado como uma cópia, e não como uma referência… Isto significa que se alterarmos o valor desse parâmetro dentro da IIFE, esse valor não vai persistir fora dela… por exemplo:

var myVar = true;

(function(myVar) {
  console.log(myVar); // true
  myVar = false;
  console.log(myVar); // false
}(myVar));

console.log(myVar); // true

Isso é bom para criarmos cópias de variaveis globais, e garantirmos que se alguem sobreescrever essa variável, isso não vai influenciar o módulo que criamos. Esse comportamento também é conhecido como Closure.

ProTip: É uma excelente prática passarmos o jQuery, window por exemplo,como parâmetros para IIFE’s.

call e apply sem medo

Esses dois caras são bem semelhantes. Ambos permitem invocar uma função em um outro contexto (que vai ser o primeiro parâmetro que você vai passar pra eles) e com os argumentos que passarmos (que serão o segundo parâmetro que passarmos). Então, call e apply permitem que:

  • 1º parâmetro => possamos dizer em qual escopo uma determinada função deve ser executada;
  • 2º parâmetro => consigamos definir os argumentos que serão passados para a função.

O que muda entre call e apply é a forma de como passar o segundo parâmetro:

  • call => uma lista de itens (a partir do 2º parâmetro);
  • apply => um array de elementos.

Para de falar Fabeni, mostra alguma coisa aí!

var mister = {
  name: 'Val Valentino',
  nickname: 'Mister M'
};

var hello = function(name, nickname) {
  return 'My name is ' + (name || this.name) + ' but you can also call me ' + (nickname || this.nickname);
};

hello.call(mister);
// "My name is Val Valentino but you can also call me Mister M"

hello.call(null, 'Raphael Fabeni', 'Fabeni');
// "My name is Raphael Fabeni but you can also call me Fabeni"

hello.apply(null, ['Raphael Fabeni', 'Fabeni']);
// "My name is Raphael Fabeni but you can also call me Fabeni"

O que temos acima é mais ou menos o seguinte:

  • um objeto simples chamado mister;
  • uma função hello que retorna uma string de acordo com os parâmetros passados;
  • as chamadas das funções usando o formato padrão e usando call e apply.

Um outro exemplo que talvez possa ajudar: vamos imaginar que possamos ter uma função simples que vai iterar sobre os argumentos dessa função (o objeto arguments). Poderíamos pensar em algo assim:

function something() {
  var likeArray = arguments;
  likeArray.forEach(function() {});

  return likeArray;
}

something('a', 'b');

Aí que vive o problema: vamos ter um erro se tentarmos algo assim.

TypeError: likeArray.forEach is not a function

Isso acontece pois o nosso brother arguments é um objeto e não um array. Pra podermos conseguir usar o forEach, precisamos converter arguments em um array e conseguimos isso utilizando o método slice. No entanto, ele é um método que pertence ao prototype de Array. Daí que vem a pergunta: como fazemos então pra executar a função/método em um outro contexto (executar slice no contexto do objeto arguments)? A resposta meu caro amigo: call ou apply.

function something() {
  var likeArray = Array.prototype.slice.call(arguments);
  likeArray.forEach(function() {});

  return likeArray;
}

No exemplo acima, alteramos a linha relacionada à variável likeArray, aplicando o método slice no contexto de arguments através do call.

Referências => Learning JavaScript

Gostou? Escrevi alguma groselha? Quer melhorar? Abra uma issue mencionando o post e vamos conversar.

Deixo aqui meu muito obrigado ao Mauricio Soares, ao Weslley Araujo e ao Frederick Silva pela revisão e contribuição no texto.

See all posts...