Programação Orientada a Objetos, Design Patterns e Performance
🎯 Objetivo: Dominar conceitos avançados de JavaScript para desenvolvimento profissional
0% concluído
Classes e Programação Orientada a Objetos
Classes são como "moldes" para criar objetos. É uma forma organizada de agrupar dados (propriedades) e ações (métodos) relacionados!
// Criando uma classe Pessoa
class Pessoa {
// Constructor: executa quando criamos uma nova pessoa
constructor(nome, idade) {
this.nome = nome;
this.idade = idade;
this.energia = 100;
}
// Método para a pessoa se apresentar
apresentar() {
return `Olá! Eu sou ${this.nome} e tenho ${this.idade} anos.`;
}
// Método para fazer exercício
exercitar() {
this.energia -= 20;
return `${this.nome} fez exercício! Energia: ${this.energia}`;
}
// Método para descansar
descansar() {
this.energia = Math.min(100, this.energia + 30);
return `${this.nome} descansou! Energia: ${this.energia}`;
}
}
// Criando objetos da classe
const pessoa1 = new Pessoa('Ana', 25);
const pessoa2 = new Pessoa('João', 30);
console.log(pessoa1.apresentar());
console.log(pessoa1.exercitar());
console.log(pessoa1.descansar());
Herança permite criar classes "filhas" que herdam características da classe "pai", mas podem ter suas próprias funcionalidades especiais!
// Classe pai (base)
class Animal {
constructor(nome, tipo) {
this.nome = nome;
this.tipo = tipo;
this.felicidade = 50;
}
comer() {
this.felicidade += 10;
return `${this.nome} comeu e está mais feliz! 😊`;
}
dormir() {
this.felicidade += 5;
return `${this.nome} dormiu bem! 😴`;
}
}
// Classe filha que herda de Animal
class Cachorro extends Animal {
constructor(nome, raca) {
super(nome, 'Cachorro'); // Chama o constructor do pai
this.raca = raca;
this.latidos = 0;
}
// Método específico do cachorro
latir() {
this.latidos++;
return `${this.nome} fez: Au au! 🐕 (${this.latidos}x)`;
}
// Sobrescrevendo método do pai
comer() {
this.felicidade += 15; // Cachorros ficam mais felizes comendo
return `${this.nome} devorou a comida! 🦴`;
}
}
// Criando um cachorro
const rex = new Cachorro('Rex', 'Golden Retriever');
console.log(rex.latir());
console.log(rex.comer());
console.log(`Felicidade: ${rex.felicidade}`);
Crie uma classe Carro com propriedades e métodos:
Tarefa: Complete a classe Carro com os métodos acelerar, frear e abastecer.
Módulos ES6 e Design Patterns
Módulos nos permitem organizar código em arquivos separados e reutilizar funcionalidades. É como ter uma caixa de ferramentas organizada!
// Simulando módulos (normalmente em arquivos separados)
// 📁 matematica.js (módulo)
const Matematica = {
// Exportando funções
somar: (a, b) => a + b,
multiplicar: (a, b) => a * b,
calcularArea: (largura, altura) => largura * altura,
// Constante exportada
PI: 3.14159
};
// 📁 utilidades.js (módulo)
const Utilidades = {
formatarMoeda: (valor) => {
return `R$ ${valor.toFixed(2).replace('.', ',')}`;
},
formatarData: (data) => {
return data.toLocaleDateString('pt-BR');
},
gerarId: () => {
return Math.random().toString(36).substr(2, 9);
}
};
// 📁 main.js (arquivo principal)
// Usando os módulos
const resultado = Matematica.somar(10, 5);
const area = Matematica.calcularArea(4, 3);
const preco = Utilidades.formatarMoeda(29.99);
const id = Utilidades.gerarId();
console.log(`Soma: ${resultado}`);
console.log(`Área: ${area}`);
console.log(`Preço: ${preco}`);
console.log(`ID gerado: ${id}`);
Design Patterns são soluções testadas para problemas comuns na programação. É como ter receitas prontas para situações específicas!
// 🏭 Factory Pattern - Fábrica de objetos
class FabricaVeiculos {
static criarVeiculo(tipo, modelo) {
switch(tipo) {
case 'carro':
return {
tipo: 'Carro',
modelo: modelo,
rodas: 4,
acelerar: () => 'Carro acelerando! 🚗'
};
case 'moto':
return {
tipo: 'Moto',
modelo: modelo,
rodas: 2,
acelerar: () => 'Moto acelerando! 🏍️'
};
case 'bicicleta':
return {
tipo: 'Bicicleta',
modelo: modelo,
rodas: 2,
acelerar: () => 'Pedalando! 🚲'
};
default:
throw new Error('Tipo de veículo não suportado');
}
}
}
// 👁️ Observer Pattern - Observador
class EventoManager {
constructor() {
this.observadores = [];
}
adicionarObservador(callback) {
this.observadores.push(callback);
}
notificar(evento) {
this.observadores.forEach(obs => obs(evento));
}
}
// Usando os patterns
const carro = FabricaVeiculos.criarVeiculo('carro', 'Civic');
const eventos = new EventoManager();
eventos.adicionarObservador(evento =>
console.log(`📢 Notificação: ${evento}`)
);
eventos.notificar('Novo veículo criado!');
console.log(carro.acelerar());
Implemente um sistema de notificações usando Observer Pattern:
Tarefa: Complete o sistema de notificações.
Performance, Debugging e Error Handling
Performance é sobre fazer seu código rodar mais rápido e usar menos recursos. Vamos ver técnicas para otimizar nosso JavaScript!
// 🐌 Código lento vs ⚡ Código otimizado
// Exemplo 1: Debounce - evita execuções excessivas
function debounce(func, delay) {
let timeoutId;
return function(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => func.apply(this, args), delay);
};
}
// Exemplo 2: Memoização - cache de resultados
function memoize(fn) {
const cache = new Map();
return function(...args) {
const key = JSON.stringify(args);
if (cache.has(key)) {
console.log('📋 Resultado do cache!');
return cache.get(key);
}
console.log('🔄 Calculando...');
const result = fn.apply(this, args);
cache.set(key, result);
return result;
};
}
// Função pesada para demonstrar memoização
const fibonacci = memoize(function(n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
});
// Exemplo 3: Medindo performance
function medirTempo(nome, funcao) {
const inicio = performance.now();
const resultado = funcao();
const fim = performance.now();
console.log(`⏱️ ${nome}: ${(fim - inicio).toFixed(2)}ms`);
return resultado;
}
// Testando performance
const resultado1 = medirTempo('Fibonacci(30)', () => fibonacci(30));
const resultado2 = medirTempo('Fibonacci(30) - Cache', () => fibonacci(30));
Tratar erros adequadamente torna nosso código mais robusto e confiável. Vamos aprender a capturar e lidar com erros!
// 🛡️ Try/Catch para capturar erros
function operacaoSegura(operacao) {
try {
console.log('🔄 Executando operação...');
const resultado = operacao();
console.log('✅ Operação bem-sucedida!');
return { sucesso: true, resultado };
} catch (error) {
console.error('❌ Erro capturado:', error.message);
return { sucesso: false, erro: error.message };
} finally {
console.log('🏁 Operação finalizada.');
}
}
// 🎯 Criando erros customizados
class ErroCustomizado extends Error {
constructor(message, codigo) {
super(message);
this.name = 'ErroCustomizado';
this.codigo = codigo;
}
}
// 🔍 Função para validar dados
function validarUsuario(usuario) {
if (!usuario) {
throw new ErroCustomizado('Usuário não fornecido', 'USER_MISSING');
}
if (!usuario.email) {
throw new ErroCustomizado('Email é obrigatório', 'EMAIL_MISSING');
}
if (usuario.idade < 0) {
throw new ErroCustomizado('Idade inválida', 'INVALID_AGE');
}
return true;
}
// 📊 Logger para debugging
const Logger = {
info: (msg) => console.log(`ℹ️ INFO: ${msg}`),
warn: (msg) => console.warn(`⚠️ WARN: ${msg}`),
error: (msg) => console.error(`❌ ERROR: ${msg}`),
debug: (msg) => console.log(`🐛 DEBUG: ${msg}`)
};
// Testando error handling
const resultado = operacaoSegura(() => {
const usuario = { nome: 'Ana', idade: 25 }; // Email faltando
validarUsuario(usuario);
return 'Usuário válido!';
});
Teste conceitos avançados de JavaScript!
// Saída aparecerá aqui...
Teste seus conhecimentos avançados!
Crie um sistema de gerenciamento de tarefas usando classes e herança.
Implemente um sistema de cache com expiração usando conceitos avançados.