Abílio Azevedo.

JavaScript

Cover Image for JavaScript
Abílio Azevedo
Abílio Azevedo

História do Javascript

Tim Berners Lee em 1989 criou a World Wide Web - Comunicação Servidor -> Navegador Nexus.

Timeline of Web Browsers Timeline of web browsers 1994 - Marc Andressen, criador do NCSA Mosaic lançou o navegador Netscape; 1995 - Brendan Eich foi recrutado para escrever uma linguagem de programação para o navegador Netscape 2.0. Então ele implementou a linguagem JavaScript (inicialmente chamada de Mocha e depois Livescript, mas pelas influencias de Java chamaram de Javascript) em 10 dias, em maio de 1995, utilizando como base as linguagens Java, Scheme, Self e com algumas influências de Perl; netscape-navigator-2-01-01 1996 - A Microsoft com o Internet Explorer copiou Javascript e lançou o JScript; 1997 - A Netscape, com medo do Jscript, padronizou a linguagem JavaScript junto a ECMA International, trocando o nome para ECMAScript;

Com a padronização, foi formado um comitê chamado de TC39 que passou a trabalhar na especificação ECMA-262 ES1 - 1997 - 110 páginas - Foi criado apenas para o browser ES2 - 1998 - 117 páginas - Adequação com a normativa ISO/IEC 16262 ES3 - 1999 - 188 páginas - Exception Handling (throw/try/catch), Regular Expression, switch, do-while Em 2005 com AJAX (Asynchronous JavaScript And XML) e o uso do JS pelos servidores, a linguagem pegou uma fama ruim Estavam trabalhando na V3.1 a Microsoft e Yahoo e na V4 Adobe, Mozilla, Opera e Google. Os projetos ficaram muito distantes e foram rejeitados pelo TC39. ES5 - 2009 - 252 páginas - JSON, strip mode, reserved words as property keys, multiline string, Object API, Array.prototype.* ES6 - ECMA2015 - 566 páginas - Class, Arrow Function, Proxy, Reflect, Map, Set, Destructuring, Rest Parameter, Default Value, Template Literal, Spread Operator, Generators, Promises, Modules ES7 - ECMA2016 - 586 páginas - Array.prototype.includes, Exponentiation operator... ES8 - ECMA2017 - 885 páginas - Async/Await, Object.values, Object.entries, String.prototype.padStart, String.prototype.padEnd, Trailling commas in parameters list, objects and arrays.

ECMA Script Versions ECMA Script compability ECMA Script Compatibility

Conceitos do Javascript

Variáveis

  • Declaração: O nome da variável é registrado no contexto de execução, também conhecido como escopo, da função
  • Inicialização: A variável é inicializada com o valor undefined
  • Atribuição: Um valor é atribuído para a variável

VAR: Ao utilizar var, a variável é declarada e inicializada no escopo da função, não respeitando bloco e permitindo a redeclaração e reatribuição LET: Ao utilizar let, a variável é declarada no escopo da função mas só é inicializada posteriormente, respeitando bloco e permitindo reatribuição mas não a redeclaração CONST: Ao utilizar const, a variável é declarada no escopo da função mas só é inicializada posteriormente, respeitando bloco e não permitindo reatribuição nem redeclaração.

Cuidado ao declarar uma variável sem VAR, LET, CONST porque ela vai para o escopo global:

(function () {
    pi = 3.141592;
})();
console.log(pi);

Identificadores de Variáveis: Um identificar válido deve começar com [a-zA-Z_$] seguido por [a-zA-Z0-9_$]: let name123; let Name123; let $name123; let _name123;

Convenções de Nomenclatura:

  • Camel Case: minhaVariavel, meuMetodo, minhaClasse
  • Pascal Case: MinhaVariavel, MeuMetodo, MinhaClasse
  • Kebab Case: minha-variavel, meu-metodo, minha-classe
  • Snake Case: minha_variavel, meu_metodo, minha_classe

Data Types: undefined, null, boolean, string, symbol, number and object

Objetos

São coleções de propriedades (atributos e métodos) que consistem em pares chave-valor São criados utilizando notação literal ou funções constructoras como new Object() São dinâmicos e as propriedades podem ser alteradas em qualquer momento

const pessoa = {
  nome: "Maria",
  idade: 20 
}

Herança

O principal objetivo da herança é permitir o reuso de código por meio do compartilhamento de propriedades entre objetos, evitando a duplicação.

Herança é baseada em protótipos de objetos e não classes. Por meio da propriedade __proto__ que referência o protótipo do objeto é possível indicar o objeto a ser herdado:

const functionalLanguage = {
paradigm: "Functional"
};
const scheme =name: "Scheme", year: 1975, _proto_: functionalLanguage
};
const javascript = {
name: "JavaScript"
year: 1995,
_proto__: functionalLanguage
}:

Se imprimirmos o objeto scheme não iremos ver a chave paradigm porque a chave está no protótipo mas se imprimirmos o scheme.paradigm vai retornar "Functional" porque ele procurar no objeto e nos protótipos.

A função hasOwnProperty checa as propriedades do objeto sem considerar as propriedades do protótipos.

Caso a mesma propriedade exista no objeto e no seu protótipo, a propriedade do próprio objeto é retornada, fazendo sombra à propriedade do protótipo.

Temos algumas funções da API de objeto para bloquear alterações no objeto: Object API

JSON

JSON, ou JavaScript Object Notation, é um formato de intercâmbio de dados, derivado da linguagem JavaScript que foi descoberto por Douglas Crockford e padronizado pela ECMA-404.

O método JSON.stringify converte um determinado tipo de dado para JSON. O método JSON.parse converte um JSON para um determinado tipo de dado. Podem ser usados para comparar objetos e para copiar objetos.

Symbol

O tipo Symbol é primitivo, único e imutável, atuando como uma chave única em um objeto.

Symbol("a") === Symbol("a") // false

Funções

Na linguagem JavaScript, tudo é baseado em funções. Instanciação com new ou (). Objeto é um conjunto de chave/valor. As funções são de primeira classe, ou seja, podem ser atribuídas a uma variável, passadas por parâmetro ou serem retornada de uma outra função.

No JavaScript, existem duas maneiras principais de definir uma função: declaração de função e expressão de função. A principal diferença entre elas reside em como elas são definidas e como são içadas (hoisted) dentro do código.

Declaração de Função:

Uma declaração de função é uma instrução que define uma função nomeada. Ela começa com a palavra-chave function seguida pelo nome da função, uma lista de parâmetros entre parênteses () e o corpo da função entre chaves {}.

function nomeDaFuncao(param1, param2) {
  // corpo da função
}

As declarações de função são içadas para o topo de seu escopo (global ou local) durante a fase de compilação. Isso significa que você pode chamar uma função declarada dessa forma antes de ela ser definida no código.

Expressão de Função:

Uma expressão de função é uma maneira de definir uma função como parte de uma expressão maior. Ela começa com a palavra-chave function seguida por um nome de função opcional, uma lista de parâmetros entre parênteses () e o corpo da função entre chaves {}. A expressão de função é frequentemente atribuída a uma variável ou passada como argumento para outra função.

const nomeDaFuncao = function(param1, param2) {
  // corpo da função
};

// ou

const outraFuncao = function funcaoNomeada(param1, param2) {
  // corpo da função
};

As expressões de função não são içadas como as declarações de função. Elas são tratadas como qualquer outra expressão e avaliadas quando a execução chega àquela linha de código. Portanto, você não pode chamar uma expressão de função antes de ela ser definida no código.

Aqui está um exemplo que ilustra a diferença:

// Declaração de Função
digaOla(); // Imprime "Olá!"

function digaOla() {
  console.log("Olá!");
}

// Expressão de Função
digaHi(); // Lança um erro: TypeError: digaHi não é uma função

const digaHi = function() {
  console.log("Hi!");
};

No exemplo acima, a declaração de função digaOla() pode ser chamada antes de ser definida porque ela é içada para o topo de seu escopo. No entanto, a expressão de função digaHi não pode ser chamada antes de ser definida porque ela não é içada.

Em geral, as declarações de função são preferidas para definir funções reutilizáveis, enquanto as expressões de função são frequentemente usadas para criar funções conforme necessário, como quando se passa uma função como argumento para outra função ou quando se define uma função dentro de outra função (closures).

Funções Anônimas:

São funções que são declaradas dinamicamente em tempo de execução. Elas são chamadas de funções anônimas porque não recebem um nome como as funções tradicionais.

const minhafn = function(param1, param2) {
  return param1 + param2;
};

minhafn(10, 20); // retorna 30

Funções geradoras: Retornam uma sequência de resultados Generator Function

Uma generator function é marcada pela assinatura com um asterisco (*) e pode conter uma ou mais chamadas usando o yield (como se fosse o "return valor" de uma função) como no exemplo abaixo:

function* myFn() {
yield 'resultado01'
yield 'resultado02'
}

Arguments Por meio da variável implícita arguments é possível acessar os parâmetros da função invocada:

const sum = function () {
  let total = 0;
  for (let argument in arguments) {
    total += arguments[argument];return total;
};
console.log(sum (1, 2, 3, 4, 5, 6, 7, 8, 91);

Classes

  • São "modelos" que encapsulam dados e comportamentos;
  • São criadas utilizando uma declaração de classe e a palavra-chave class;
  • Geralmente seguem o paradigma de OO, com herança, polimorfismo, etc;
  • Uma vez criada, não pode ser alterada dinamicamente;
class Pessoa {
  constructor(nome, idade) {
    this.nome = nome;
    this.idade = idade;
  }
}

const maria = new Pessoa("Maria", 20);

Palavras chaves das classes:

  • Public: Interface publica para ser acoplada. Esses membros da classe estão disponíveis para todos que podem acessar a instância da classe (proprietária).
  • Private: Esses membros só são acessíveis na classe que instanciou o objeto. Reduzir acoplamento (não expor muitos detalhes de implementação)
  • Protected: Esta palavra-chave permite um pouco mais de acesso do que membros privados, mas muito menos do que o público. Um membro protegido é acessível dentro da classe (semelhante ao privado) e em qualquer objeto que herde dele. Um valor protegido é compartilhado por todas as camadas da cadeia de protótipos. Não é acessível a mais ninguém.

Prototype: Conseguimos modificar a funcionalidade da função/objeto/variáveis.

function Person(first, last, age, eyecolor) {
  this.firstName = first;
  this.lastName = last;
  this.age = age;
  this.eyeColor = eyecolor;
}

Person.prototype.name = function() {
  return this.firstName + " " + this.lastName;
};

Date

O padrão ISO 8601 estabelece um padrão para representação de datas no formato string. Abaixo apresentarei alguns formatos de acordo com o padrão ISO 8601 e o que acontece quando os utilizamos para instanciar o objeto da classe Date em JavaScript.

console.log (new Date('2021-12-15'));
// Saída: Date Tue Dec 14 2021 21:00:00 GMT-0300 (Horário Padrão de Brasília)

Quando não especificamos a hora, minuto ou, opcionalmente, o segundo, a norma ISO estabelece o fuso horário da data é considerado o UTC (Coordinated Universal Time - ou Tempo Universal Coordenado, em português)

console. log (new Date( 2021-12-15T00:00Z'));
// Saída: Date Tue Dec 14 2021 21:00:00 GMT-0300 (Horário Padrão de Brasília)

A saída é a mesma do código anterior. A diferença é que especificamos a hora e minuto e, ao colocarmos o caractere Z maiúsculo no final, estamos explicitando que o fuso horário é o UTC.

console. Log (new Date ('2021-12-15T00: 00'));
// Saída: Date Wed Dec 15 2021 00:00:00 GMT-0300 (Horário Padrão de Brasília)

Quando especificamos a hora, minuto e, opcionalmente, o segundo, o padrão define que é assumido que se a data e hora se refere ao fuso horário local.

Array

.forEach: Itera sobre os elementos de um array:

array.forEach(el => {
  // faz algo com el  
});

.filter: Filtra um array baseado em uma condição, retornando outro array:

const filtrados = array.filter(el => el > 2); 

.map: Mapeia valores para um novo array a partir de um array existente:

const dobrados = array.map(el => el * 2);

.reduce: Reduz um array a um único valor acumulando os elementos através de uma função:

const soma = array.reduce((acumulador, el) => acumulador + el, 0);

.sort: Ordena os elementos de um array in-place:

array.sort((a, b) => a - b); // crescente
array.sort((a, b) => b - a); // decrescente  

Array methods cheatsheet

Operadores Aritméticos

Soma + Subtração - Multiplicação * Divisão / Resto %

Operadores de Atribuição

Soma += Subtração -= Multiplicação *= Divisão /= Resto %=

Operadores de comparação

  • Igual (==) - Retorna verdadeiro se os valores dos dois operandos forem iguais.
x == y
  • Diferente (!=) - Retorna verdadeiro se os valores dos dois operandos forem diferentes.
x != y
  • Maior que (>) - Retorna verdadeiro se o operando da esquerda for maior que o da direita.
x > y
  • Menor que (<) - Retorna verdadeiro se o operando da esquerda for menor que o da direita.
x &lt; y 
  • Maior ou igual (>=) - Retorna verdadeiro se o operando da esquerda for maior ou igual ao da direita.
x &gt;= y
  • Menor ou igual (<=) - Retorna verdadeiro se o operando da esquerda for menor ou igual ao da direita.
x &lt;= y

Esses operadores compara dois valores e retornam um resultado booleano (verdadeiro ou falso) que pode ser usado em estruturas condicionais e de repetição.

A diferença principal entre os operadores == e ===

  • O operador == compara apenas o valor dos operandos, fazendo coerção de tipo se necessário. Por exemplo:
5 == "5" // retorna true

Aqui o JavaScript converte a string "5" para número antes da comparação.

  • Já o operador === compara valor e tipo dos operandos. É uma comparação mais rígida. Exemplo:
5 === "5" // retorna false

Como um operando é número e outro é string, retorna falso mesmo ambos tendo valor "5".

Em resumo:

  • == compara valores fazendo coerção de tipo
  • === compara valores e tipos, sem conversão

Por isso em geral o uso de === é preferível para evitar comportamentos inesperados. Mas às vezes a coerção do == também pode ser útil.

Operadores Binários

Ou: | E: & Ou exclusivo (XOR): ^ Negação (not): ~ Deslocamento para esquerda (shift) << Basicamente é uma multiplicação por 2 multiplicado pelo valor de deslocamento.

> 4 << 2
16
> (4).toString(2).padStart(32,0)
'00000000000000000000000000000100'
> (16).toString(2).padStart(32,0)
'00000000000000000000000000010000'

Deslocamento para direita (shift) >> Basicamente é uma divisão por 2 multiplicado pelo valor de deslocamento.

> 128 >> 1
64
> (128).toString(2).padStart(32,0)
'00000000000000000000000010000000'
> (16).toString(2).padStart(32,0)
'00000000000000000000000001000000'

Deslocamento para direira com mudança de sinal(shift) >>>

Outros Operadores

Operador && para IF: O operador && (E lógico) avalia a expressão da esquerda e só avalia a expressão da direita se a da esquerda for verdadeira. Isso permite retornos condicionais como:

function foo(algo) {
  return algo && algo.metodo(); 
}

Operador || para IF: O operador || (Ou lógico) avalia a expressão da esquerda e só avalia a expressão da direita se a da esquerda for falsa. Isso permite retornos condicionais como:

function foo(algo) {
  return algo.metodo2() || algo.metodo(); 
}

For: Laço de repetição para iterar sobre estruturas de dados:

for(let i = 0; i < array.length; i++) {
  // faz algo
}

Operador spread: O operador spread (...) "espalha" as propriedades de um objeto ou os elementos de um array em outro. Útil para clonar e combinar:

// Clonando
const novoArray = [...arrayOriginal]; 
const novoObjeto = {...objetoOriginal}; 

// Combinando arrays
const combinadoArray = [...array1, ...array2];
const combinadoObjeto = {...objeto1, ...objeto2};

Operador Destructuring: Permite extrair valores de arrays ou propriedades de objetos em variáveis distintas:

// Array
const [a, b] = [10, 20];

// Objeto  
const {prop1, prop2} = {prop1: 10, prop2: 20};

Conversões numéricas

Nem todos os operadores numéricos realizam a conversão (coersão de tipos) desejada:

&gt; &quot;10&quot; + 0 
&quot;100&quot;
&gt; &quot;10&quot; - 5
5

Algumas conversões podem perder informação

&gt;parseInt(&quot;9.9&quot;,10)
9
&gt;parseFloat(&quot;0xFF&quot;)
0
&gt;parseFloat(&quot;0b10&quot;)
0

IEEE 754

O IEEE 754 é um padrão de representação numérica criado em 1985 e adotado por diversas linguagens de programação como o JavaScript, Ruby, Python e Java. IEEE 754 Single Floating Point Format.svg

Pode ocorrer errors de arredondamento:

&gt; 0.1 + 0.2
0.30000000000000004
&gt; 666.7 - 666.6
0.10000000000002274
&gt; 33.3 * 3
9989999999999999
&gt; 12.2 / 0.1
121.99999999999999

Você pode conferir na calculadora IEEE 754.

Infinity, que pode ser positivo ou negativo, é retornado quando uma operação ultrapassa os limites do tipo number.

&gt; 1/0;
Infinity
&gt; Math.pow(10, 1000);
Infinity
&gt; Number.MAX_VALUE * 2
Infinity
&gt; Math.log(0);
-Infinity
&gt;-Number.MAX_VALUE * 2
-Infinity

NaN, ou Not a Number, é retornado quando realizamos uma operação numérica onde não é possível determinar o resultado. Ele não dispara um erro, continua o fluxo.

> 10 *&quot;Javascript&quot;;
NaN
&gt; 0/0;
NaN
&gt; Math.sqrt(-9);
NaN
&gt; Math.log(-1);
NaN
&gt; parseFloat(&quot;JavaScript&quot;);
NaN

String

Em JavaScript, existem dois tipos principais de strings:

  1. String literal (string "normal"): São strings criadas colocando o texto entre aspas simples ('') ou aspas duplas (""). Por exemplo:
let string1 = 'Isso é uma string'; 
let string2 = "Também é uma string";

As strings literais são os tipos de strings mais comuns em JavaScript.

  1. Objeto String: O objeto String em JavaScript provê métodos e propriedades adicionais para trabalhar com strings. Por exemplo:
let s = new String("Hello World");
console.log(s.length); // 11

Aqui nós criamos um objeto String usando o construtor String e então acessamos a propriedade length desse objeto.

A principal diferença é que as strings literais são mais simples e diretas, enquanto o objeto String permite funcionalidades estendidas.

Mas na maioria dos casos, as strings literais são suficientes, sendo o uso do objeto String menos comum. Algumas diferenças importantes:

  • Strings literais possuem melhor performance
  • Objetos String podem ter métodos adicionados e comportamentos modificados, enquanto strings literais são mais imutáveis.

Então em resumo, para a maioria dos propósitos, focar em usar strings literais é o ideal para trabalhar com strings em JavaScript. O objeto String é mais avançado e raramente necessário e menos performático:

Usando construtor Usando somente aspas

let counter = 0;
console.time("performance"); 
while(counter < 100000){
  new String("JavaScript");
  counter++;
}
console.timeEnd("performance");


let counter = 0;
console.time("performance"); 
while(counter < 100000){
  "JavaScript";
  counter++;
}
console.timeEnd("performance");
    
performance: 5.56884765625 ms performance: 1.032958984375 ms

Math

Math é um objeto global que contém constantes matemática e métodos para a realização de operações envolvendo números.

&gt; Math.E;
2.718281828459045
&gt; Math.LN10; //Logaritmo natural de 10
2.302585092994046
&gt; Math.LN2; // Logaritmo natural de 2
0.6931471805599453
&gt; Math.LOG10E; // Logaritmo de E na base 10
0.4342944819032518
&gt; Math.LOG2E; // Logaritmo de E na base 2
1.4426950408889634
&gt; Math.PI;
3.141592653589793
&gt; Math.SQRT1_2; // Raiz quadrada de 1/2
0.7071067811865476
&gt; Math.SQRT2; // Raiz quadrada de 2
1.4142135623730951
  • abs: Converte o sinal do número para positivo;
  • ceil: Arredonda o número para cima;
  • floor: Arredonda o número para baixo;
  • round: Arredonda o número para cima se a parte decimal for de 5 a 9 e para baixo se for de 0 a 4;
  • sign: Retorna 1 se o número for positivo e -1 se for negativo;
  • trunc: Elimina a parte decimal do número, tornando-o um inteiro;
  • min: Retorna o menor número passado por parâmetro
&gt; Math.min(1,2,3,4,5,6)
1
  • max: Retorna o maior número passado por parâmetro
&gt; Math.max(1,2,3,4,5,6)
6
  • random: Retorna um número randômico entre 0 e 1, não incluindo o 1

Regex

As expressões regulares são estruturas formadas por uma sequência de caracteres que especificam um padrão formal que servem para validar, extrair ou mesmo substituir caracteres dentro de uma String.

Temos duas formas de representar uma expressão regular:

let regExp1 = new RegExp("john@gmail.com")
let regExp2 = /john@gmail.com/;

Podemos testar:

let result = regExp.test("john@gmail.com");
console.log(result); //true

Podemos executar:

let result = regExp.exec("john@gmail.com");
console.log(result); //['john@gmail.com', index: 0, input: 'john@gmail.com', groups: undefined]

Metacaracteres: são caracteres com funções específicas, que informam padrões e posições impossíveis de serem especificadas com caracteres normais.

^ - Inicia com um determinado caractere

$ - Finaliza com um determinado caractere

- A barra é utilizada antes de caracteres especiais, com o objetivo de escapá-los

[abc] - Aceita qualquer caractere dentro do grupo, nesse caso a, b e c

[!abc] - Não aceita qualquer caractere dentro do grupo, nesse caso a, b ou c

[0-9] - Aceita qualquer caractere entre 0 e 9

[^0-9] - Não aceita qualquer caractere entre 0 e 9

\w - Representa o conjunto [a-zA-Z0-9_]

\W - Representa o conjunto [^a-zA-Z0-9_]

\d - Representa o conjunto [0-9]

\D - Representa o conjunto [^0-9]

\s - Representa um espaço em branco

\S - Representa um não espaço em branco

\n - Representa uma quebra de linha

\t - Representa um tab

Os quantificadores podem ser aplicados a caracteres, grupos, conjuntos ou metacaracteres.

{n} - Quantifica um número específico {n,} = Quantifica um número mínimo

{n,m} - Quantifica um número mínimo e um número máximo

?- Zero ou um

*- Zero ou mais

    • Um ou mais podem ser aplicados a caracteres, grupos, conjuntos ou metacaracteres.

{n} - Quantifica um número específico {n,} = Quantifica um número mínimo

{n,m} - Quantifica um número mínimo e um número máximo

?- Zero ou um

*- Zero ou mais

+- Um ou mais

Grupos de Captura () - Determina um grupo de captura para realizar a extração de valores de uma determinada String

this

“This” pode variar -> Em arrow function o this é fixo Depende do contexto léxico, o this é relacionado ao contexto.

function f1()  { console.log(this == window)}
function f2() { console.log(this == body)}
const f3 = () => console.log(this==window)

Bind: Força um determinado contexto para o this. Útil quando se perde o contexto correto de this:

No JavaScript, apenas batendo o olho é difícil adivinhar o contexto ".this" das funções. Isso porque elas podem ser executadas no futuro e essa é a maior causa para aqueles erros de "undefined is not a function".

Diferente do que muitos dizem, a solução é mais simples do que você pode imaginar. O que você precisa ter em mente é: quem irá executar aquela função no futuro?

Um grande exemplo é o evento “click” do browser, vamos dar uma olhada nesse cenário:

const instance = {
    name: 'test',
    myOnClick() {
      console.log('name', this.name)
   }
}
window.addEventListener('click', instance.myOnClick)

A função myOnClick, do objeto instance, será disparada no futuro e vai herdar o "this" de window, pois o addEventListener faz parte do contexto de window. Sendo assim, a propriedade "name" não vai existir e teremos um resultado "undefined". Aí que entra a função .bind. Com ela, você configura, exatamente, de onde a função vai herdar o contexto this. Então, para resolver o problema, basta "bindar" o instance, de forma manual, como contexto:

window.addEventListener('click', instance.myOnClick.bind(instance))

Isso funciona para casos de classes, objetos e, principalmente, para quando você precisa converter um callback para Promise, no Node.js, algo como:

await util.promisify(fs.write.bind(fs))(...args)

Tagged templates

Tagged templates (ou template tags) são uma funcionalidade avançada do JavaScript que permite processar os valores de um template literal com uma função customizada.

A função usada para processar o template literal é chamada de "tag function". Ela recebe como parâmetros os strings estáticos do template e os valores interpolados.

function format(partes, ...valores) {
  const formatted = valores.map(valor => {
    if(typeof valor === 'number') {
      return valor.toLocaleString('pt-BR');
    }

    return valor;  
  });

  return partes.reduce((str, parte, i) => {    
    return `${str}${parte}${formatted[i] || ''}`; 
  }, '');
}

const preco = 1000.99; 
const mensagem = format`O preço é R$ ${preco}`; 

console.log(mensagem); // O preço é R$ 1.000,99

Desta forma, a tag format customizada aplica a formatação apropriada aos valores interpolados no template literal.

Isso facilita muito reutilizar e padronizar regras de output de strings em uma aplicação.

Assincronicidade

A assincronicidade em JavaScript permite que o código seja executado de forma não bloqueante (já que o javascript é single threaded), melhorando a performance e experiência do usuário. No entanto, apenas retornar uma Promise não torna o código assíncrono automaticamente.

Vejamos um exemplo:

console.log("Antes da função assíncrona");

async function loopMilhoesDeVezes() {
  for(let i = 0; i < 1000000; i++) {
    // alguma tarefa simples 
  }

  console.log("Loop finalizado");
}

loopMilhoesDeVezes().then(() => 
  console.log("Promise retornada")
);

console.log("Depois da função assíncrona");

O resultado será:

Antes da função assíncrona
Loop finalizado
Depois da função assíncrona
Promise retornada

Apesar de retornarmos uma Promise, todo o loop irá bloquear a execução do código até finalizar. Isso porque não estamos utilizando nenhuma API assíncrona interna do JavaScript.

Para tornar isso realmente assíncrono, podemos utilizar o setTimeout:

console.log("Antes da função assíncrona");

async function loopAssincrono() {
  return new Promise((resolve)=> setTimeout(() => {
    for(let i = 0; i <= 1000_000; i++){
      // something simple
    }
    // loop demorado
    console.log("Loop finalizado"); 
    resolve();
  }, 0))
}

loopAssincrono().then(() => 
  console.log("Promise retornada")  
);

console.log("Depois da função assíncrona"); 

O resultado será:

Antes da função assíncrona
Depois da função assíncrona
Loop finalizado
Promise retornada

Dessa forma, o loop será agendado para execução futura, permitindo que o código continue normalmente.

Outras APIs assíncronas comuns em JavaScript incluem fetch, requestAnimationFrame, setInterval e muito mais. Utilizando elas corretamente, podemos escrever códigos assíncronos de alto desempenho em JavaScript.

Compiladores

Babel é um compilador JavaScript. É usado para converter o código ES6 em uma versão JS compatível para diferentes ambientes.

Nem tudo suporta ES6. Se você estiver usando uma sintaxe moderna como React, precisará do Babel em mãos.

Esperamos que todos os navegadores suportem ES6 um dia. Mas por enquanto, para garantir que tudo funcione perfeitamente, o Babel não pode faltar no seu projeto.

O módulo de dobramento (module folding) ou tree-shaking é uma técnica de otimização que remove código Javascript não utilizado durante a construção de uma aplicação antes de colocá-la em produção.

Ela analisa os arquivos da aplicação para identificar exports e imports não utilizados de módulos Javascript. Em seguida, elimina esses recursos não referenciados, reduzindo o tamanho do pacote Javascript compilado sem afetar funcionalidades.

Dessa forma, tree-shaking remove o "código morto" e deixa apenas os recursos realmente necessários para executar a aplicação, melhorando desempenho de carregamento e uso de memória. É muito útil em aplicações modernas que utilizam grandes bibliotecas modulares de Javascript.

Bundler

O webpack é um module bundler, ou empacotador de módulos, para aplicações JavaScript. Algumas de suas principais funcionalidades e benefícios são:

  • Empacotamento (bundling) - O webpack pega todos os arquivos e dependências Javascript, CSS, imagens, fonts e outros e os empacota em um ou mais bundles otimizados para produção. Isso melhora a performance ao reduzir a quantidade de requests.
  • Loaders - Os loaders do webpack permitem que você integre uma variedade de linguagens e pré-processadores diferentes no seu pipeline de build, como JSX, TypeScript, SASS, Less etc.
  • Code splitting - O webpack permite dividir seu código em múltiplos bundles que podem ser carregados sob demanda ou em paralelo, melhorando o tempo de carregamento da página.
  • Minificação e otimização de código - O webpack pode minificar e otimizar automaticamente todo o Javascript, CSS, HTML e imagens da sua aplicação para produção através de plugins.
  • Hot Module Replacement - O HMR atualiza módulos no browser em tempo real sem precisar atualizar toda a página, melhorando bastante a experiência de desenvolvimento.
  • Ambiente e build simplificados - O webpack cuida de todo o processo de build de desenvolvimento e produção, configuração de tasks, ambientes e muito mais.

Interpretadores

Um interpretador Javascript lê o código fonte Javascript linha por linha e o executa imediatamente, sem uma etapa de compilação separada. À medida que o interpretador percorre o código, ele aloca memória para variáveis e funções e as associa aos seus respectivos escopos.

Hoisting refere-se ao comportamento do interpretador Javascript de mover as declarações de funções, variáveis e classes para o topo de seus escopos. Isso acontece porque o interpretador faz duas passagens sobre o código:

Na primeira passagem, o interpretador "iça" todas as declarações de funções e variáveis para o topo de seus escopos. As funções são inteiramente movidas, enquanto apenas as declarações das variáveis são movidas, não as atribuições.

Na segunda passagem, o código é executado linha por linha. Como as declarações já foram movidas para o topo, quaisquer referências às funções e variáveis podem ser resolvidas pelo interpretador.

O hoisting permite que funções e variáveis sejam referenciadas em seu escopo antes de serem declaradas. No entanto, o valor inicial das variáveis içadas é undefined até que a linha da atribuição seja executada.

O comportamento de hoisting é importante de entender para evitar bugs inesperados. Ao declarar funções e variáveis no topo do escopo, conforme uma boa prática, o comportamento de hoisting é mais intuitivo e previsível. Hoisting Makes This Work


Mais posts

Cover Image for Documentos Técnicos

Documentos Técnicos

Aprenda a importância vital da documentação técnica abrangente para projetos de software em crescimento. Descubra as melhores práticas, como Requests for Comments (RFCs) e Architectural Decision Records (ADRs), que promovem transparência, colaboração e registro de decisões arquiteturais. Explore ferramentas poderosas como wiki.js e Backstage para criar centros de documentação eficazes. Mantenha seu projeto organizado, compreensível e sustentável com essa abordagem à documentação técnica.

Abílio Azevedo
Abílio Azevedo
Cover Image for Superlógica - BFF para o Gruvi

Superlógica - BFF para o Gruvi

Construindo um BFF (Backend for Frontend) para o SuperApp Gruvi que tem mais de 120 mil usuários ativos e milhões de possíveis usuários para disponibilizar no ecossistema Superlogica.

Abílio Azevedo
Abílio Azevedo

NewsLetter

Eu enviarei o conteúdo postado aqui no blog. Sem Spam =)

Engenheiro de software experiente, formado em Engenharia Elétrica, com mais de 10 anos de experiência prática na construção de aplicativos móveis, web e back-end robustos e escaláveis em vários projetos, principalmente no setor de fintech. Mobile (React Native), Web (React e Next.JS) e Backend (Node.JS, PHP e DJANGO). Meu objetivo é criar produtos que agreguem valor às pessoas. - © 2024, Abílio Azevedo