Estendendo o JavaScript

Quem já acompanha o blog há mais de uma semana ou duas, notou que as postagens aumentaram de uns tempos pra cá. Os motivos que contribuiram para isso são muitos. O primeiro deles é que estou com FDQF (Falta Do Que Fazer), fim de bimestre, todos projetos terminados e entregues (os mais urgentes)… E outro que vale a pena ser citado: aumentou o incentivo para continuar escrevendo. Ontem registrei, em média, 50 visitantes, em lugar dos 18 a 25 habituais (avanço, não?). Não sei quanto vai durar, mas deu um “turbo boost” nas postagens. Cortando o papo furado, vamos ao que realmente interessa.

Meu propósito não é literalmente estender o JavaScript, mas sim alguns de seus objetos nativos, através da POO, aumentando a capacidade dessa linguagem.

Como estender os objetos?

Você teve coragem de perguntar isso? Espero que não. Como já disse na parte 2 sobre JavaScript OO, estendemos qualquer objeto através de prototype. Qualquer objeto mesmo, seja ele criado por nós mesmos ou nativo do JavaScript.

O meu objetivo aqui não é criar métodos extraordinários, é apenas estende-los, tornando algumas funções mais rápidas, economizando código. O negócio é aproveitar ao máximo os recursos que a linguagem nos dá.

Estendendo o Array

O objeto array é muito utilizado na programação. Hoje daremos um uso “incomum” para ele. Transformaremos ele em uma query string. Não é nada muito avançado, já que o JavaScript não nos dá tantas opções. O resultado será: 0=java&1=script, ou seja, um parâmetro (índice numérico do array) com seu valor (valor do array). Vamos ao trabalho:

var nossoArray = new Array("java","script");	// Aqui é o nosso array
Array.prototype.toQueryString = function() { };

Como diria Jack Estripador, vamos por partes. Primeiramente, criamos um simples array. A segunda linha que é o grande segredo: estendemos o objeto array, adicionando um método toQueryString. Fácil como tirar um doce de uma criança.

É, realmente muito fácil. A parte complicada vem exatamente agora. Depois dela tudo fica fácil novamente. Não é exatamente parte do script, mas sim uma questão de lógica. E a pergunta que vale 1 milhão é: … … … como vamos pegar os valores do array, se não passamos nenhum argumento, o array não pode ser acessado por nossoArray (até pode, mas a POO vai por água abaixo, o objetivo é a reutilização de código), e agora?

Analisemos: quando criamos um array, o que ele retorna?

var nossoArray = new Array("java","script");
alert(nossoArray); // Retorno do Array

Isso mesmo, um objeto array retorna ele mesmo. Como referenciamos algo que se refere a si mesmo? this. Agora a parte fácil: juntar tudo com “&”. Vamos preencher nossa função:

var nossoArray = new Array("java","script");	// Aqui é o nosso array
Array.prototype.toQueryString = function() {
	var queryString = "";
	for(var i = 0; i < this.length; i++) {
		if(this[i].indexOf("&") < 0 && queryString.length > 0) queryString += "&" + i + "=" + this[i];
		else queryString += i + "=" + this[i];
	}
	return queryString;
};
var nossaQueryString = nossoArray.toQueryString();
alert(nossaQueryString);

Nada de complicado. Como já disse esses dias: pensar é a alma do negócio. Pode não ser, e realmente não é, a melhor função que você já viu na vida, mas se você entender o conceito, terá um mundo de aventuras pela frente.

Estendendo Strings

Agora vamos fazer o reverso do nosso método. Transformar query strings em arrays associativos. Como já sabemos, o objeto String retorna a string em si, como nos arrays. Vamos aplicar isso em nosso método:

String.prototype.toArray = function() {
	var arrayQuery = Array();
	var tempArray = this.split("&");
	for(var i = 0; i < tempArray.length; i++) {
		var anotherArray = tempArray[i].split("=");
		arrayQuery[anotherArray[0]] = anotherArray[1];
	}
	return arrayQuery;
}

Mais simples ainda, e mais eficiente, já que os índices do array retornado aqui também podem ser strings (nossoArray['primeiro_par']). Acho que não há nada de extraordinário. Vamos adiante.

Estendendo Objetos

Objetos são as coisas mais complicadas de se estender. Quase tudo em JavaScript pode ser tratado como um objeto. Trabalharemos aqui somente com tags, os objetos DOM. Adicionaremos um método que transforma um elemento para negrito, ou para fonte normal.

Essa técnica foi testada apenas no Firefox. Para estender objetos DOM de uma maneira cross-browser, leia: Estendendo Objetos DOM.

Como temos que desobrir com qual objeto estamos trabalhando, devemos nos assegurar que trabalhamos com uma tag. Lembre-se de testar o objeto com o qual se trabalha, para ter certeza de que não haverá funcionamento indesejado.

Nosso código:

Object.prototype.Strong = function(bold) {
	if(this.tagName) {	// Se for tag...
		var weight = bold == true ? "bold" : "normal";	// True = bold; false = normal
		this.style.fontWeight = weight;		// Aplicando o estilo
	}
	return this;	// Retornando o elemento
}

O objeto seria algo do tipo document.getElementById(). Não funciona no IE, servindo só pra exemplo mesmo (quem souber fazer funcionar no IE e puder me dizer eu agradeço).

Outros objetos tornam-se mais difíceis de serem estendidos. Resta a você aprender a utilizar. Existe uma infinidade de coisas para se aprender ainda.

Ressaltando: nota agora a vantagem de se usar a POO? O código pode ser reutilizado em muitas situações, inclusive o próprio código nativo. Boa sorte no desbravamento do mundo prototype!

22 de outubro, 2006

Escopo de Variáveis

Vamos entrar em um tema agora muito simples do JavaScript, mas que muita gente esquece, principalmente em projetos pequenos: escopo.

O escopo de uma variável é o alcance que ela tem, de onde pode ser acessada (não confunda com encapsulamento). Esse escopo pode ser global ou local.

No escopo global, como o próprio nome diz, a variável pode ser acessada de qualquer lugar do script, seja de fora de tudo, dentro de funções, dentro de laços, tudo. Diferente do PHP, JavaScript não necessita que as variáveis globais sejam declaradas dentro de funções. Basta usa-las normalmente. Veja um exemplo de uma variável global:

var global = "Variável Global";
function testGlobal() {	alert(global); }

Familiar, não? Também podem ser declaradas sem o “var” na frente. Na verdade, não há segredo algum, você já faz isso desde suas primeiras linhas em JavaScript.

O escopo local é um tanto relativo. Uma variável local pode ser acessada de um mesmo nível ou níveis inferiores, e nunca superiores. Veja:

function local() {
	var local = "Variavel Local";
	alert(local);
}
alert(local);

O primeiro alert (se chamado) funcionará perfeitamente, já o segundo (de fora da função) retornará em um erro. Agora preste atenção: para declarar variáveis locais, precisamos obrigatoriamente incluir o var, senão as variáveis passarão para o escopo global.

Tá certo, isso não é o melhor tutorial do mundo, nem teve muita utilidade. Até agora. Pense em um script onde muitas variáveis utilizam o mesmo nome, em funções diferentes. Seria um problema, não? Observe:

var i = 3;
function errado() {
	while(i < 15) { i++; }
	alert(i);
}
function certo() {
	var i = 3;
	while(i < 15) { i++; }
	alert(i);
}
alert(i);	// Retorna 3
certo();	// Retorna 15
alert(i);	// Ainda retorna 3
errado();	// Retorna 15, tudo certo
alert(i);	// Ops, retorna 15!

Viu? É importante tomar cuidado com esses pequenos detalhes. Já pensou se o seu script dependesse dessa variável i, e em uma simples função ela vai pelos ares (ou melhor, pelos 15)?

E este é o motivo pelo qual usamos for(var i = 0;i < x; i++), declarando o i.

Diferencie-se, cuide dos pequenos detalhes. E boa sorte!

21 de outubro, 2006

Ativando o Botão Voltar

Depois de alguns aprimoramentos, essa técnica se tornou muito melhor. Como esse post está desatualizado, considere ler sua continuação: Histórico para Navegação via JavaScript.

Depois de um longo tempo de pesquisa, mais um tempinho para testes e melhoramentos, descobri um script que nos permite manipular o botão voltar. Como em Ajax a navegação é completamente por JavaScript, o botão voltar não é ativado de maneira alguma, certo? É, quase. Existe um código extremamente complicado que faz o botão ser ativado. Nem vou explica-lo, vou deixar vocês sofrerem pra descobrir:

top.location = "#hash";

Esse é equivalente a <a href=”#hash”>. Como sabemos, âncoras (ou hashes) ativam o botão voltar. Tudo certo, eureka, descobrimos a América, um viva para nós. Mas existe um pequeno inconveniente: quando clicamos no botão voltar, ele não ativa nenhum evento no navegador. Isso mesmo, pode procurar em todas as referências que você conhece.

E agora??? Volte ao início da teoria do JavaScript. Você lembrará de uma simples função:

setInterval("history(false)", 100);

Isso te faz pensar em algo? Uma função executada a cada 1/10 de segundo? Ou qualquer outro intervalo pequeno. É esse “artifício” (legítima gambiarra, mas necessária) que usaremos para verificar se o usuário voltou, avançou ou recarregou a página.

Agora vamos montar um pedaço de nosso script:

var local = "";
window.onLoad = setInterval("history(false)", 100);
function request( x ) {
	ajax(url + "?id=" + x); // Aqui uma função que faça a requisição Ajax
	top.location = "#" + x; // Pode ser trocado por um link com hash
	history(true); // Chama a função de histórico
}

Agora preste atenção em alguns detalhes importantes: a função history foi chamada duas vezes, com false e outra vez com true. Esse parâmetro serve para dizer a função que o usuário (se for true) chamou a função ou foi pelo intervalo (caso for false). Outro detalhe: a variável “local” é a variável que dirá a função history em qual local (na página, lembre-se disso) estamos.

Curioso? Então vamos adiante, para a outra função do nosso script: history. Apertem os cintos e vamos decolar. Se você tem estômago fraco ou é cardíaco, recomendo… continuar, não tem nada de assustador, é extremamente simples:

function history(fromUser) {
  localHash = location.hash.split("#")[1];
  if (fromUser){
    local = localHash;
  }
  else if (local != localHash){
        request(localHash);
  }
}

Agora preste atenção novamente. Lembra do “local” (eu avisei pra não esquecer…)? Agora temos outro local, “localHash”, que é o local para onde o hash aponta. O nosso hash servirá de query string, se já deu pra notar. O que está no hash vai para a outra função em determinada situação.

Se “fromUser” for true (veio do usuário), não nos preocupamos e passamos localHash direto para o nosso local. Se for via intervalo, verificamos se “local” é igual a “localHash”. Se não for, executamos uma nova requisição. E é só isso! Apenas 9 linhas de código para a nossa função de histórico! Acabamos com todas as 7 cabeças do dragão. Espero que saibam usar, já que para muitas query strings o negócio fica mais complicado. Bom proveito!

Depois de alguns aprimoramentos, essa técnica se tornou muito melhor. Como esse post está desatualizado, considere ler sua continuação: Histórico para Navegação via JavaScript.

20 de outubro, 2006

Pense Mais e Codifique Menos

Durante o desenvolvimento da versão 1.2 do meu Ajax FrameWorX, passei por algumas tantas dificuldades na programação. Isso é completamente normal, todo o desenvolvedor passa por isso, sem excessões. Falhas, defeitos, erros, inconsistências e a lista não termina por aí. O que interessa é o que se faz quando passamos por essas dificuldades.

Essa é a parte que difere um desenvolvedor de outro. O que devo fazer? Que tipo de técnica usar para contornar um problema? Muitos desenvolvedores, principalmente os desenvolvedores brasileiros (não sei, acho que vai da índole do povo), apelam para a conhecida POG (Programação Orientada a Gambiarras). Os desenvolvedores mais dedicados e mais preparados apelam para a melhor solução, a solução certa.

Isso não depende tanto do conhecimento (apesar de ajudar bastante), já que o que vale é saber pensar. Quando alguma coisa dá errado, não devemos (e não podemos) fazer da maneira mais fácil e rápida (me refiro as gambiarras aqui). Devemos pensar. Na Internet de hoje em dia, e na do futuro principalmente, economizar linhas de código é extremamente importante. Mais importante ainda é economizar linhas de código sem que nada deixe de funcionar.

Essas características, acredito eu, são as que realmente diferenciam um desenvolvedor de outro. Não desistir porque algo errado aconteceu, e sim contornar o problema da melhor maneira possível, sem desistir no meio de um projeto.

Por essas e outras, estude, atualize-se, pesquise, estude, analise, pense, estude, estude, estude, estude. Isso com certeza vai te tornar um excelente desenvolvedor, e vai te ajudar bastante, da mesma maneira que está me ajudando a crescer como desenvolvedor. E isso vale pra JavaScript, PHP, ASP, CGI, XHTML, CSS, enfim, tudo isso aí. Boa Sorte em sua jornada!

20 de outubro, 2006

PHP Orientado a Objeto

Desde que escrevi esse artigo, aprendi muito sobre programação orientada a objetos. Sugiro que não use esse post como referência. Se desejar, “JavaScript Orientado a Objetos” é uma fonte mais correta e atualizada sobre POO.

Eu sei que PHP é um tanto fora do contexto do blog, mas pra quem tiver a fim, fica aí a dica. E os conceitos servem também pra JavaScript Orientado a Objeto.

Que diabos é Programação Orientada a Objeto?

A Orientação a Objetos é uma maneira de programar que modela os processos de programação de uma maneira próxima a realidade, tratando cada componente de uma aplicação (script) como um objeto, com suas características e funcionalidades.

Nada impede que se programe dessa maneira na versão 4 do PHP, mas a versão 5 reescreveu o tratamento de objetos, permitindo mais recursos, performance e vantagens no uso deste tipo de programação.

Classes e Objetos

Classe é a estrutura fundamental para a criação de um objeto. Uma classe é um conjunto organizado de variáveis (propriedades ou atributos) e métodos (funções), que será utilizada como um novo tipo e instanciará um objeto. Uma classe tem por objetivo criar um objeto, que é uma representação desta classe em uma variável.

Vamos utilizar a seguinte classe:

<?php
// Classe dog
class dog {
	public $nome;
	public $humor;
	function setNome( $nome )
	{
		$this->nome = $nome;
	}
	function setHumor( $humor )
	{
		$this->humor = $humor;
	}
	function falar()
	{
		echo $this->nome . ' está ' . $this->humor . ' e disse: AUAU!';
	}
}
?>

Aí criamos uma classe que instanciará um objeto, no nosso caso, um cachorro. Ela tem duas propriedades ($nome e $humor), e três métodos (setNome($nome), setHumor($humor) e falar()). Agora, criando um objeto a partir dessa classe, vamos ver o que podemos fazer com ela:

$cachorro = new dog; // Instanciamos o objeto "cachorro"
$cachorro->setNome( "Rex" ); // Setamos $cachorro->nome;
$cachorro->setHumor( "feliz" ); // Setamos $cachorro->humor;
$cachorro->falar(); // Chamamos o método $cachorro->falar();

Como você deve ter visto, instanciamos um objeto utilizando o operador new. Para utilizarmos as propriedades e métodos da classe, devemos utilizar o operador ->, como vimos acima.

A variável $this

Na definição da classe, podemos usar a variável $this, que é o próprio objeto. Quando uma classe é instanciada em um objeto, e utilizamos a variável $this, essa variável se refere ao objeto que estamos utilizando.

Herança

Herança é uma forma de reutilização de código em que novas classes são criadas a partir de classes já existentes, herdando atributos e métodos, e incluindo outros que sejam de necessidade. Vamos extender nossa classe dog, suportando também raças.

class raca extends dog
{
	public $raca;
	function setRaca( $raca )
	{
		$this->raca = $raca;
	}
	function falar()
	{
		echo $this->nome . ' é da raça ' . $this->raca . ' e disse: AUAU!';
	}
}

A classe raca, acima, herdou todas as propriedades e métodos da sua classe pai, dog. Além disso, foi adicionado um método setRaca($raca), e o método falar() foi modificado. Usando sub-classes, é possível redefinir métodos e propriedades, e acrescentar outros, dependendo das necessidades.

Espero que tenha dado uma boa base. Logo entraremos em JavaScript também…

27 de setembro, 2006