JavaScript - Características

-

Recursos Principais

O JavaScript possui vários recursos principais que o tornam uma linguagem de programação poderosa e flexível para desenvolvimento web. Esses recursos incluem variáveis e tipos de dados, operadores, fluxo de controle e funções.

Variáveis e Tipos de Dados

No JavaScript, você pode declarar variáveis usando as palavras-chave var, let ou const. var é a forma tradicional de declarar variáveis, enquanto let e const foram introduzidas no ES6 (ECMAScript 2015) para fornecer escopo de bloco e imutabilidade.

O JavaScript possui vários tipos de dados primitivos:

Tipo de Dado Descrição
number Representa valores numéricos, tanto inteiros quanto números de ponto flutuante
string Representa dados textuais, entre aspas simples ou duplas
boolean Representa um valor lógico, true ou false
null Representa um valor nulo deliberado
undefined Representa uma variável que foi declarada, mas não recebeu um valor

O JavaScript também possui um tipo de dado object, que pode conter coleções de pares chave-valor ou estruturas mais complexas.

JavaScript é uma linguagem de tipagem dinâmica, o que significa que as variáveis podem conter valores de qualquer tipo de dado, e o tipo de uma variável pode mudar durante a execução. Essa flexibilidade é alcançada por meio de coerção e conversão de tipos, onde o JavaScript converte automaticamente valores de um tipo para outro quando necessário.

Operadores

O JavaScript fornece uma variedade de operadores para realizar operações em valores:

  • Operadores aritméticos: +, -, *, /, %, ++, --
  • Operadores de atribuição: =, +=, -=, *=, /=, %=
  • Operadores de comparação: ==, ===, !=, !==, >, <, >=, <=
  • Operadores lógicos: &&, ||, !
  • Operador ternário: condição ? valor1 : valor2

Fluxo de Controle

O JavaScript oferece várias declarações de fluxo de controle que permitem controlar a execução do código com base em certas condições ou repetir blocos de código várias vezes.

  • Declarações if...else: Executam um bloco de código se uma condição especificada for verdadeira, ou outro bloco de código se a condição for falsa
  • Declaração switch: Avalia uma expressão e executa o bloco de código correspondente com base no caso correspondente
  • Loop for: Repete um bloco de código por um número especificado de vezes
  • Loops while e do...while: Repetem um bloco de código enquanto uma condição especificada for verdadeira
  • Declarações break e continue: Alteram o fluxo dos loops, terminando o loop ou pulando para a próxima iteração

Funções

Funções são blocos de código reutilizáveis que executam tarefas específicas. No JavaScript, você pode definir funções usando a palavra-chave function seguida pelo nome da função e um conjunto de parênteses contendo parâmetros opcionais.

Exemplo: Declaração de Função

function saudacao(nome) {
  console.log("Olá, " + nome + "!");
}

Funções podem aceitar parâmetros como entrada e retornar valores usando a palavra-chave return. Elas podem ser chamadas pelo seu nome seguido de parênteses contendo quaisquer argumentos necessários.

O JavaScript também suporta expressões de função, onde as funções podem ser atribuídas a variáveis ou passadas como argumentos para outras funções.

Exemplo: Expressão de Função

const quadrado = function(x) {
  return x * x;
};

As funções de seta, introduzidas no ES6, fornecem uma sintaxe concisa para definir funções, particularmente quando usadas como callbacks ou em padrões de programação funcional.

Exemplo: Função de Seta

const multiplicar = (a, b) => a * b;

Esses recursos principais formam a base do JavaScript e são essenciais para escrever programas básicos a complexos no desenvolvimento web. Entender e dominar esses recursos permitirá que você crie aplicações web dinâmicas e interativas.

Recursos Avançados

JavaScript possui muitos recursos avançados que permitem escrever código mais complexo e eficiente. Esses recursos incluem objetos e arrays, classes e herança, programação assíncrona e tratamento de erros.

Objetos e Arrays

Objetos e arrays são estruturas de dados importantes em JavaScript. Objetos armazenam coleções de pares chave-valor, enquanto arrays armazenam listas ordenadas de valores.

Para criar um objeto, você pode usar a notação literal de objeto ou o construtor Object. Você pode acessar e modificar propriedades do objeto usando a notação de ponto ou a notação de colchetes.

Exemplo: Criando e acessando propriedades de objetos

const pessoa = {
  nome: "João",
  idade: 30,
  cidade: "São Paulo"
};

console.log(pessoa.nome); // Saída: "João"
pessoa.idade = 31;
console.log(pessoa["cidade"]); // Saída: "São Paulo"

Arrays são criados usando a notação literal de array ou o construtor Array. Você pode acessar e modificar elementos do array usando seus índices.

Exemplo: Criando e acessando elementos de arrays

const frutas = ["maçã", "banana", "laranja"];

console.log(frutas[0]); // Saída: "maçã"
frutas[1] = "uva";
console.log(frutas); // Saída: ["maçã", "uva", "laranja"]

Objetos e arrays possuem métodos integrados que permitem realizar operações comuns, como adicionar ou remover elementos, buscar valores e modificar dados.

JavaScript também suporta desestruturação, que permite extrair valores de objetos e arrays e atribuí-los a variáveis de forma concisa.

Exemplo: Desestruturação de objetos e arrays

const { nome, idade } = pessoa;
console.log(nome); // Saída: "João"

const [primeiro, segundo] = frutas;
console.log(segundo); // Saída: "uva"

Classes e Herança

JavaScript suporta programação orientada a objetos por meio de classes e herança. Classes são modelos para criar objetos com propriedades e métodos específicos.

Para definir uma classe, você usa a palavra-chave class seguida pelo nome da classe. Dentro da classe, você pode definir um método construtor para inicializar as propriedades do objeto e outros métodos para definir o comportamento do objeto.

Exemplo: Definindo uma classe

class Retangulo {
  constructor(largura, altura) {
    this.largura = largura;
    this.altura = altura;
  }

  calcularArea() {
    return this.largura * this.altura;
  }
}

Você pode criar instâncias de uma classe usando a palavra-chave new seguida pelo nome da classe e quaisquer argumentos necessários.

Exemplo: Criando uma instância de classe

const ret = new Retangulo(5, 3);
console.log(ret.calcularArea()); // Saída: 15

JavaScript também suporta herança, permitindo criar novas classes baseadas em classes existentes. Você pode usar a palavra-chave extends para criar uma subclasse que herda propriedades e métodos de uma superclasse.

Exemplo: Herança em JavaScript

class Quadrado extends Retangulo {
  constructor(lado) {
    super(lado, lado);
  }
}

const quadrado = new Quadrado(4);
console.log(quadrado.calcularArea()); // Saída: 16

Subclasses podem sobrescrever métodos da superclasse usando o mesmo nome de método. Elas também podem chamar o método da superclasse usando a palavra-chave super.

Programação Assíncrona

JavaScript é uma linguagem de thread único, o que significa que o código é executado sequencialmente. No entanto, JavaScript fornece maneiras de lidar com operações assíncronas, como fazer requisições HTTP ou ler arquivos, sem bloquear a execução de outro código.

No passado, a programação assíncrona em JavaScript era feita usando callbacks. Um callback é uma função passada como argumento para outra função e é executada quando a operação assíncrona é concluída.

Exemplo: Programação assíncrona com callbacks

function buscarDados(callback) {
  // Simulando uma operação assíncrona
  setTimeout(() => {
    const dados = "Olá, mundo!";
    callback(dados);
  }, 1000);
}

buscarDados((dados) => {
  console.log(dados); // Saída: "Olá, mundo!"
});

No entanto, callbacks podem levar a código aninhado e complexo, conhecido como "callback hell". Para resolver esse problema, JavaScript introduziu Promises, que fornecem uma maneira melhor de lidar com operações assíncronas.

Promises representam a eventual conclusão ou falha de uma operação assíncrona e permitem encadear múltiplas operações assíncronas usando os métodos .then() e .catch().

Exemplo: Programação assíncrona com Promises

function buscarDados() {
  return new Promise((resolve, reject) => {
    // Simulando uma operação assíncrona
    setTimeout(() => {
      const dados = "Olá, mundo!";
      resolve(dados);
    }, 1000);
  });
}

buscarDados()
  .then((dados) => {
    console.log(dados); // Saída: "Olá, mundo!"
  })
  .catch((erro) => {
    console.error(erro);
  });

O ES2017 introduziu as palavras-chave async e await, que fornecem uma sintaxe que parece mais com código síncrono para trabalhar com Promises. Dentro de uma função async, você pode usar a palavra-chave await para pausar a execução até que uma Promise seja resolvida.

Exemplo: Programação assíncrona com async/await

async function buscarDados() {
  // Simulando uma operação assíncrona
  const dados = await new Promise((resolve) => {
    setTimeout(() => {
      resolve("Olá, mundo!");
    }, 1000);
  });
  return dados;
}

buscarDados().then((dados) => {
  console.log(dados); // Saída: "Olá, mundo!"
});

O loop de eventos e a pilha de chamadas do JavaScript trabalham juntos para gerenciar a execução de código síncrono e assíncrono. O loop de eventos verifica continuamente a pilha de chamadas e a fila de tarefas, executando tarefas da fila de tarefas quando a pilha de chamadas está vazia.

Tratamento de Erros

O tratamento de erros é uma parte importante da escrita de código JavaScript robusto. JavaScript fornece a declaração try...catch para lidar com exceções que podem ocorrer durante a execução do código.

Você pode envolver o código que pode lançar um erro dentro de um bloco try, e especificar o código para lidar com o erro no bloco catch.

Exemplo: Tratamento de erros com try...catch

try {
  // Código que pode lançar um erro
  throw new Error("Algo deu errado!");
} catch (erro) {
  console.error(erro.message); // Saída: "Algo deu errado!"
}

Você pode usar a palavra-chave throw para lançar seus próprios erros ou objetos de erro personalizados. Isso permite lidar com tipos específicos de erros de maneira diferente.

Exemplo: Lançando erros personalizados

function dividir(a, b) {
  if (b === 0) {
    throw new Error("Divisão por zero!");
  }
  return a / b;
}

try {
  console.log(dividir(10, 0));
} catch (erro) {
  console.error(erro.message); // Saída: "Divisão por zero!"
}

Recursos do ES6+

O JavaScript evoluiu ao longo dos anos, e o ECMAScript 2015 (ES6) introduziu novos recursos que se tornaram amplamente adotados no desenvolvimento moderno de JavaScript. Esses recursos visam tornar a linguagem mais expressiva, concisa e fácil de trabalhar. Aqui estão alguns dos recursos notáveis do ES6+.

Template Literals

Template literals, também conhecidos como template strings, oferecem uma maneira de criar e manipular strings em JavaScript. Eles apresentam duas vantagens principais sobre a concatenação tradicional de strings:

  1. Strings Multi-linha: Os template literals permitem criar strings multi-linha sem a necessidade de caracteres de escape ou concatenação. Você pode envolver o conteúdo da sua string em crases ( ) e incluir quebras de linha diretamente dentro da string.

Exemplo: String Multi-linha

const stringMultiLinha = `
  Isto é uma
  string multi-linha
  usando template literals.
`;
  1. Interpolação de String: Os template literals suportam interpolação de string, o que permite incorporar expressões ou variáveis diretamente dentro da string. Ao envolver a expressão ou variável em ${}, você pode incorporar valores dinâmicos em suas strings.

Exemplo: Interpolação de String

const nome = "João";
const idade = 30;
const mensagem = `Meu nome é ${nome} e eu tenho ${idade} anos.`;
console.log(mensagem); // Saída: "Meu nome é João e eu tenho 30 anos."

Atribuição por Desestruturação

A atribuição por desestruturação é uma forma de extrair valores de objetos ou arrays e atribuí-los a variáveis. Ela fornece uma sintaxe concisa para desempacotar valores de estruturas de dados.

  1. Desestruturação de Objeto: A desestruturação de objeto permite extrair propriedades específicas de um objeto e atribuí-las a variáveis com um nome correspondente.

Exemplo: Desestruturação de Objeto

const pessoa = {
  nome: "João",
  idade: 30,
  cidade: "São Paulo"
};

const { nome, idade } = pessoa;
console.log(nome); // Saída: "João"
console.log(idade); // Saída: 30
  1. Desestruturação de Array: A desestruturação de array permite extrair valores de um array com base em sua posição e atribuí-los a variáveis.

Exemplo: Desestruturação de Array

const numeros = [1, 2, 3, 4, 5];
const [a, b, ...resto] = numeros;
console.log(a); // Saída: 1
console.log(b); // Saída: 2
console.log(resto); // Saída: [3, 4, 5]

A atribuição por desestruturação também suporta valores padrão, que são usados quando o valor extraído é indefinido, e permite renomear variáveis durante o processo de atribuição.

Arrow Functions

As arrow functions fornecem uma sintaxe concisa para definir funções em JavaScript. Elas oferecem uma alternativa compacta às expressões de função tradicionais.

  1. Sintaxe Concisa: As arrow functions eliminam a necessidade da palavra-chave function e usam a sintaxe => para definir a função.

Exemplo: Arrow Function - Sintaxe Concisa

const saudacao = (nome) => {
  return `Olá, ${nome}!`;
};
console.log(saudacao("João")); // Saída: "Olá, João!"

Se o corpo da função consistir em uma única expressão, você pode omitir as chaves e a palavra-chave return, tornando a sintaxe ainda mais concisa.

Exemplo: Arrow Function - Expressão Única

const quadrado = (x) => x * x;
console.log(quadrado(5)); // Saída: 25
  1. Vinculação Léxica do this: As arrow functions têm uma vinculação léxica do this, o que significa que elas herdam o valor do this do escopo circundante. Esse comportamento é útil ao trabalhar com métodos de objetos ou funções de callback.

Exemplo: Arrow Function - Vinculação Léxica do 'this'

const pessoa = {
  nome: "João",
  saudacao: function() {
    setTimeout(() => {
      console.log(`Olá, ${this.nome}!`);
    }, 1000);
  }
};
pessoa.saudacao(); // Saída: "Olá, João!" (após 1 segundo)

Módulos

O ES6 introduziu um sistema de módulos padronizado para JavaScript, permitindo organizar seu código em módulos reutilizáveis e encapsulados. Os módulos fornecem uma maneira de definir e compartilhar código em diferentes arquivos ou projetos.

  1. Exportando Módulos: Para tornar um valor, função ou classe disponível para uso em outros módulos, você precisa exportá-lo usando a palavra-chave export.

Exemplo: Exportando Módulos

// matematica.js
export function soma(a, b) {
  return a + b;
}

export const PI = 3.14159;
  1. Importando Módulos: Para usar valores, funções ou classes de outro módulo, você precisa importá-los usando a palavra-chave import.

Exemplo: Importando Módulos

// principal.js
import { soma, PI } from './matematica.js';

console.log(soma(2, 3)); // Saída: 5
console.log(PI); // Saída: 3.14159

Os módulos também suportam exportações padrão, que permitem exportar um único valor como a exportação padrão de um módulo, e exportações nomeadas, que permitem exportar vários valores nomeados de um módulo.

Operadores Rest e Spread

Os operadores rest e spread (...) fornecem uma maneira de trabalhar com vários elementos de forma concisa.

  1. Parâmetros Rest: O operador rest permite capturar vários argumentos passados para uma função como um array.

Exemplo: Parâmetros Rest

function soma(...numeros) {
  return numeros.reduce((acc, curr) => acc + curr, 0);
}
console.log(soma(1, 2, 3, 4, 5)); // Saída: 15
  1. Operador Spread: O operador spread permite espalhar os elementos de um array ou objeto em outro array ou objeto.

Exemplo: Operador Spread

const numeros = [1, 2, 3];
const novosNumeros = [...numeros, 4, 5];
console.log(novosNumeros); // Saída: [1, 2, 3, 4, 5]

const pessoa = { nome: "João", idade: 30 };
const novaPessoa = { ...pessoa, cidade: "São Paulo" };
console.log(novaPessoa); // Saída: { nome: "João", idade: 30, cidade: "São Paulo" }

Estes são alguns dos recursos notáveis do ES6+ que foram introduzidos no JavaScript. Outros recursos incluem parâmetros padrão de função, literais de objeto aprimorados, classes, promessas e mais. Esses recursos melhoraram muito a expressividade, legibilidade e funcionalidade da linguagem, tornando o desenvolvimento JavaScript mais agradável e eficiente.