Javascript orientado a objetos
09/05/2007 por Leandro Mercês Xavier
Para um melhor entendimento deste artigo, é necessário algum conhecimento sobre programação orientada a objetos, existem vários artigos na web e bons livros dedicados ao assunto. Tomemos como exemplo de objeto, um ventilador. Ao olharmos para este objeto, podemos identificá-lo dentre outros eletrodomésticos pelas suas características. Outros ventiladores podem apresentar características idênticas, porém são objetos distintos. Um ventilador pode estar desligado ou ligado em algumas velocidades. Detalhes de sua estrutura ficam ocultos internamente, pois não precisamos conhecê-los para fazer uso do mesmo.
Javascript difere-se de linguagens clássicas orientadas a objeto como Java e C++ principalmente por não possuir uma definição formal de classe. Entretanto possui seu próprio tipo de herança baseada em protótipo e faz uso constante de objetos baseando-se nesse tipo de herança.
Função construtora e propriedades / Classe e atributos
Na programação orientada a objetos, é comum utilizar tipos de objetos (classes) personalizados. Essas classes1 são úteis em diversos casos, por exemplo, se seu programa manipula várias formas geométricas, poderiam haver várias classes de objetos como quadrados, retângulos e círculos. Utilizando nosso exemplo, vamos criar a função construtora Ventilador, contendo as propriedades2 velocidadeMaxima e ligado. Observe que a propriedade velocidadeMaxima recebe o valor do argumento velMax.
function Ventilador(velMax) {
this.velocidadeMaxima = velMax;
this.ligado = false;
}
A palavra-chave this é responsável por iniciar o objeto adequadamente.
Instâncias
A criação de um objeto, ou seja, a instanciação de uma classe é realizada com uso do operador new. Após este operador vem o nome da função construtora, responsável pela inicialização do objeto.
Alguns exemplos de instanciação:
var obj = new Object();
var data = new Date();
Seguindo mais uma vez o exemplo do ventilador:
var ventilador1 = new Ventilador(3);
Acessando propriedades
Para acessar as propriedades de um objeto você deve utilizar o operador “.” que deve ser precedido de uma referência ao objeto e sucedido pelo nome de uma de suas propriedades.
alert(ventilador1.velocidadeMaxima); // Retorna 3
Diferente das linguagens clássicas orientadas a objeto, Javascript permite que propriedades sejam adicionadas a qualquer momento durante a execução do código. Por exemplo, vamos adicionar a propriedade cor a nosso ventilador:
ventilador1.cor = "branco";
alert(ventilador1.cor); // Retorna branco
Métodos
Métodos em Javascript são funções invocadas por objetos. Para criar um novo método, basta atribuir uma função a um nome no objeto utilizando também o operador “.” como ocorre com as propriedades. O exemplo abaixo demonstra como definir o método ligar para a classe Ventilador utilizando a função liga através da propriedade prototype.
function liga() {
this.ligado = true;
}
Ventilador.prototype.ligar = liga;
Caso queira adicionar um método a um objeto em particular, pode fazê-lo da seguinte maneira:
ventilador2 = new Ventilador(2);
ventilador2.ligar = liga;
Outro uso possível é definir o método na estrutura da classe:
function liga() {
this.ligado = true;
}
function Ventilador(velMax) {
this.velocidadeMaxima = velMax;
this.ligado = false;
this.ligar = liga;
}
A palavra chave this é substituída pelo objeto que invoca a função, essa é uma das principais vantagens da utilização de métodos. Exemplo de uso:
var ventilador = new Ventilador(3);
alert(ventilador.ligado); // Retorna false
ventilador.ligar();
alert(ventilador.ligado); // Retorna true
Literais de objeto
Os literais de objeto3 possibilitam criar e iniciar objetos de uma maneira diferente. A sintaxe é definida por uma lista de pares nome/valor separados por vírgulas entre um par de chaves. Cada par nome/valor é definido pelo nome da propriedade seguido de dois pontos e do valor correspondente.
var Livro = {
titulo : "Os Três Mosqueteiros",
autor : "Alexandre Dumas",
capitulo1 : {
titulo : "Os três presentes do sr. D'Artagnan pai",
paginas : 11
},
capitulo2 : {
titulo : "A antecâmara do sr. Tréville",
paginas : 8
}
}
// Acessando as propriedades:
alert(Livro.titulo + " - " + Livro.autor + "\\n\\t" +
Livro.capitulo1.titulo + " - " +
Livro.capitulo1.paginas + " páginas\\n\\t" +
Livro.capitulo2.titulo + " - " +
Livro.capitulo2.paginas + " páginas");
Composição
A composição é um recurso utilizado para definir uma relação do tipo “tem um” (“has a” relationship), ou seja, um objeto que conta com outros objetos para formar sua estrutura. Por exemplo, um objeto do tipo Carro teria em sua estrutura objetos do tipo Roda, Volante, Banco. O exemplo anterior que descreve um livro, também demonstra o uso deste recurso.
function Livro(titulo, autor) {
this.titulo = titulo;
this.autor = autor;
}
function Capitulo(titulo, paginas) {
this.titulo = titulo;
this.paginas = paginas;
}
var livro = new Livro("Os Três Mosqueteiros", "Alexandre Dumas");
var capitulo1 = new Capitulo("Os três presentes do sr. D'Artagnan pai", 11);
var capitulo2 = new Capitulo("A antecâmara do sr. Tréville", 8);
// Os objetos do tipo Capitulo fazem parte da composição do objeto livro
livro.capitulo1 = capitulo1;
livro.capitulo2 = capitulo2;
// Acessando as propriedades:
alert(livro.titulo + " - " + livro.autor + "\\n\\t" +
livro.capitulo1.titulo + " - " +
livro.capitulo1.paginas + " páginas\\n\\t" +
livro.capitulo2.titulo + " - " +
livro.capitulo2.paginas + " páginas");
Encapsulamento
Como exposto no início do artigo, em nosso exemplo que representa um ventilador, detalhes da estrutura de alguns objetos ficam ocultos internamente, pois não precisamos conhecê-los para fazer uso dos mesmos. O encapsulamento tem por objetivo esconder essa informação que não precisa ser de conhecimento do utilizador da classe. Seu uso é uma boa prática quanto à manutenção da classe, pois podemos modificar a parte que é oculta ao utilizador sem alterar sua forma de implementação. Em Javascript podemos usar encapsulamento em propriedades de uma classe utilizando (ou não) a palavra-chave var ao invés da palavra-chave this e do operador “.”.
function Ventilador(velMax) {
var maximaPermitida = 5; // Uso de encapsulamento
var velocidadePadrao = 3; // Variáveis privadas
// Avalia se a velocidade máxima fornecida é maior que zero e menor que 5, o limite atual.
if (velMax > 0 && velMax <= maximaPermitida) {
// Caso seja, atribui o valor fornecido à propriedade velocidadeMaxima
this.velocidadeMaxima = velMax;
} else {
// Caso contrário, atribui o valor da variável velocidadePadrao à propriedade velocidadeMaxima
this.velocidadeMaxima = velocidadePadrao;
}
this.ligado = false;
this.ligar = function() { // O método ligar agora é definido
this.ligado = true; // por um literal de função, o que
} // melhora a legibilidade do código.
}
ventilador = new Ventilador(0); // Cria a instância fornecendo o valor 0 para o argumento velMax;
alert(ventilador.velocidadeMaxima); // Retorna 3 – o padrão
alert(ventilador.maximaPermitida); // Retorna undefined
Herança
Em Javascript a herança ocorre por meio de objetos protótipos4 e define uma relação do tipo “é um” (“is a” relationship). Cada objeto herda propriedades e métodos de seu objeto protótipo que é referenciado pela propriedade prototype. A classe Object é a superclasse de todas as classes definidas em Javascript, ou seja, todos os construtores criados herdam propriedades e métodos definidos no construtor Object() como por exemplo o método toString(), que assim como outros pode ser sobrescrito na subclasse. Em alguns casos, é conveniente utilizar este recurso em classes personalizadas, para isso basta definir um construtor como valor para a propriedade prototype da classe em questão. Como exemplo simplório, vamos definir a classe Eletrodomestico com a propriedade ligado e os métodos ligar e desligar comuns a todos os eletrodomésticos e então definir a classe Ventilador com propriedades e métodos peculiares.
function Eletrodomestico() {
this.ligado = false;
this.ligar = function() {
this.ligado = true;
}
this.desligar = function() {
this.ligado = false;
}
}
function Ventilador(velMax) {
var maximaPermitida = 5; // Uso de encapsulamento
var velocidadePadrao = 3; // Variáveis privadas
if (velMax > 0 && velMax <= maximaPermitida) {
this.velocidadeMaxima = velMax;
} else {
this.velocidadeMaxima = velocidadePadrao;
}
}
Ventilador.prototype = new Eletrodomestico(); // Define o objeto protótipo
ventilador = new Ventilador(4);
alert(ventilador.ligado); // Retorna false
ventilador.ligar();
alert(ventilador.ligado); // Retorna true
A utilização do objeto protótipo faz com que a propriedade constructor também seja herdada da superclasse, o que definiria a classe Eletrodomestico como valor da propriedade no objeto ventilador. Uma alternativa é definir de forma explícita a propriedade constructor:
Ventilador.prototype.constructor = Ventilador;
Conclusão
Este artigo é apenas um incentivo à adoção dos conceitos da orientação a objetos em Javascript. Com o entendimento dos conceitos, os desenvolvedores podem corroborar as vantagens em códigos mais complexos, organizando o desenvolvimento e facilitando a manutenção dos scripts.
Notas
1. Termo usado informalmente no artigo.
2. Nas linguagens clássicas orientadas a objeto, geralmente usa-se o termo atributo ou campo.
3. Conhecidos também como inicializadores de objetos ou objetos literais, implementados a partir de Javascript 1.2.
4. Implementados a partir de Javascript 1.1.
publicado em
11/05/07 às 22:43
Nossa, muito bom mesmo, bem explicado pra caramba
13/05/07 às 12:32
Também escrevi uma série sobre isso, mas você fez de uma maneira bem mais compacta.
Gostei da parte da herança, que achava ainda meio obscura em JavaScript.
Parabéns
13/05/07 às 20:07
Ae Leandro..
Muito boa explicação, gostei bastante.
Parabéns!!!
Abraço
16/05/07 às 10:51
Leandro, meus parabéns!
Suas explicações estão muito didáticas mesmo…
Gostei do modo como vc compactou um assunto que da um nó na cabeça de muita gente…
=)
abraços
16/05/07 às 13:59
Leandro,
Bem legal a maneira que você mostrou de como se pode usar orientação a objetos com javascript. Até então eu ainda não conhecia nenhum artigo em português que colocasse isso de maneira prática.
Parabéns!!!
Michel Santos
29/05/07 às 15:14
Pow muito legal Leandro.
Tirei altas dúvidas.
FAloww, Filippe Brito
31/05/07 às 3:44
Muito bom!! Parabéns pelo artigo!
Abordou o tema de forma clara e objetiva! Só tinha visto material assim em inglês.
Abraços,
Aldo
7/06/07 às 14:09
Muito bom kra! Meus parabéns mesmo. Você conseguiu colocar os conseitos de OO de forma clara e objetiva. Você poderia me indicar um bon livro sobre javaScript? ( Que trata todos esses conseitos da web 2.0 ) Tipo javaScript Avançado
7/06/07 às 17:39
Obrigado a todos pelos elogios. É bom saber que esse artigo é bem recebido, espero que os próximos sobre temas tão importantes quanto este também sejam.
Filipe Acácio, o único livro que indico é “JavaScript - O guia definitivo” de David Flanagan. É um livro bem abrangente e trata JavaScript como a linguagem de programação que é, não apenas uma linguagem para adicionar algumas ações às páginas web. Além disso contou com o apoio de Brendan Eich, que respondeu à perguntas, leu e forneceu comentários quanto à algumas edições (1ª e 3ª), segundo o próprio autor. http://www.davidflanagan.com/
Abraços.
2/07/07 às 17:27
Muittooo boa explicação, explicou um dos conceitos de OO e ainda como usa-los na sintaxe de javaScript….
Parabéns !!!
16/07/07 às 22:48
Olá, apreciei bastante o tutorial, meu ajudou num projeto no qual estou trabalhando. Só tenho uma dúvida: se não declararmos a propriedade constructor, como na linha
“”Ventilador.prototype.constructor = Ventilador;”"
qual problemas posso ter? Eu testei sem essa linha e aparentemente tudo funcionou.
Aguardo uma resposta, obrigado!
19/07/07 às 8:54
Olá Karlisson, não há problema algum. Na verdade a propriedade constructor faz referência à função construtora, em casos de herança convém redefini-la por questões de padronização e semântica.
25/07/07 às 19:08
Excelente artigo. Na verdade está mais claro que o material do w3c. Parabéns.
ps: utilizando literais posso referenciar funções?
25/07/07 às 22:16
Gostei muito do artigo!
Também publiquei uma série de artigos no meu blog. MAs a forma que você apresentou foi bem suscinta, porém concisa!
Parabéns!
3/09/07 às 10:59
Excelente artigo.
Uma ótima referência, bookmarked!
———————————–
http://www.inov9.com
10/10/07 às 10:13
Bacana o assunto, boa a didática, acredito que será possível o melhor entendimento e organização do código JavaScript se utilizarmos os conceitos de Orientação a Objetos.
1/11/07 às 22:10
Parabéns,
Uma explicação excelênte muito clara e objetiva.
=)
14/11/07 às 17:09
Esse artigo é um achado.
Muito bem escrito e com um assunto tão relevante.
Eu estou com um problema para implementar uma classe de cronômetro, pois ela usa o comando:
setInterval(”this.atualiza”, 1000);
ou seja, ele tem que executar um método da classe atualiza() a cada segundo. Porém, o comando aceito pelo setInterval() deve ser global e isso está me dando uma baita duma dor de cabeça.
Como eu faço nesse caso?
18/11/07 às 13:29
Olá Daniel, neste caso específico o erro ocorre pelo modo como você está invocando o método setInterval.
Ele possui duas sintaxes válidas:
setInterval(expression, msec)
setInterval(function, msec[, arg1[, …, argN]])
Você está utilizando a primeira, onde fornece uma expressão entre aspas como argumento. Provavelmente quando a expressão é resolvida o this faz referência ao objeto window, do qual setInterval é um método.
Nesse caso, basta utilizar sem as aspas, passando apenas o método como argumento para que funcione. Um exemplo simplório:
function Cronometro() {
this.atualiza = function() {
document.getElementById(\”sec\”).innerHTML = new Date().getSeconds();
}
this.start = function() {
setInterval(this.atualiza, 1000);
}
}
window.onload = function() {
var c = new Cronometro();
c.start();
}
Porém, quando definimos uma função dentro de outra a palavra-chave this referencia ao objeto global (window) e não à função na qual está contida.
function Cronometro() {
this.atualiza = function() {
document.getElementById(\”sec\”).innerHTML = new Date().getSeconds();
}
function start() {
setInterval(this.atualiza, 1000); // Faz referência a window.atualiza
}
start(); // Aviso: reference to undefined property this.atualiza
// Erro: useless setInterval call (missing quotes around argument?)
}
window.onload = function() {
var c = new Cronometro();
}
Quando isso ocorre, é necessário criar uma propriedade que faça referência ao this. Por exemplo:
function Cronometro() {
var self = this;
this.atualiza = function() {
document.getElementById(\”sec\”).innerHTML = new Date().getSeconds();
}
function start() {
setInterval(self.atualiza, 1000);
}
start();
}
window.onload = function() {
var c = new Cronometro();
}
19/12/07 às 11:34
Leandro, seu tutorial ficou ótimo, muito bem elaborado e explicativo. Parabéns e obrigado pela iniciativa. Abraço.
30/01/08 às 16:01
Muito bom.