Recentemente, comprei o livro Javascript – the Good Parts, do Douglas Crockford. A principal motivação, não vou negar, foi o node.js (e o fato que muitas pessoas na net têm falado de Javascript como uma das mais “novas” maravilhas. Mas isso fica pra outro post). O livro, embora tenha uma série de problemas (e o principal é a ausência de exemplos claros, além de dar pouca ênfase ao “Javascript way”, na minha opinião) é muito bom e traz algumas definições no mínimo diferentes sobre Javascript.
Basicamente, o que é Javascript? Javascript é orientado a objeto? Javascript é funcional? Javascript suporta classes, suporta….?
Para começar, Javascript é uma linguagem de programação com sintaxe semelhante a C++, dinâmica, com tipagem absurdamente fraca, pseudo-orientada a objetos, orientada a protótipos e funcional. Wow… um de cada vez.
Dinâmica, e tipagem fraca é evidente: 1 + “1” retorna “11”. Dá pra fazer castings com Number() e String(), embora isso normalmente não é necessário (ou, pelo menos, é o que nos querem fazer acreditar). O ponto é que Javascript sequer checa o número de argumentos numa função, por exemplo. Logo, o código abaixo seria um código válido, mesmo não chamando a função com dois parâmetros (para rodar, rode no firebug console, do Firefox):
function printSomething(a, b) { console.log(a); } printSomething(10);
Pseudo orientada a objetos significa que Javascript suporta alguma coisa semelhante à orientação a objetos (Crockford chama isso de “pseudoclassical”), porém não é recomendado. Javascript também possui uma falha muito grande, e é o fato de que tudo é orientado a variáveis globais. Dá para driblar isso, e caímos em outra falha: não existem variáveis ou métodos privados, tudo é público. Dá pra driblar isso também, e caímos em mais uma coisa: COMO? Isso vem ao outro ponto: funcional.
Javascript suporta o paradigma funcional de programação. Isso significa, na prática, suporte à funções anônimas e “closures”. A diferença entre elas é sutil (e no Javascript, virtualmente inexistente), e basta dizer que uma função introduz um novo escopo de variável, mas ao mesmo tempo mantém o “binding” da função anterior. Para declarar uma função anônima, basta declará-la sem nome:
//Define uma função anônima: function(a) { console.log(a); } //Define uma função anônima, e já a roda: (function(a) { console.log(a); })('Hello, world!');
Funções anônimas podem ser atribuídas a uma variável:
var funcao = function(e) { console.log(e); }
E, por fim, funções podem ser passadas entre si:
var forEach = function(array, f) { for(var i = 0; i < array.length; i++) { f(array[i]); } } //O código a seguir imprime 4, 5, 6 na tela forEach([4, 5, 6], function(e) { console.log(e); });
Além disso, as funções mantém os “bindings” do escopo anterior:
//Imprime: //Valor de X: Zê //Valor de Y: Ypslon //Valor de X: Xis //Valor de Y: Ypslon (function () { var x = 'Xis', y = 'Ypslon'; (function () { var x = 'Zê'; console.log("Valor de X: " + x); console.log("Valor de Y: " + y); })(); console.log("Valor de X: " + x); console.log("Valor de Y: " + y); })();
Como eles mantém o “binding”, é possível simular o funcionamento de um método/variável privado:
var criarContador = function() { var c = 0; var somar = function() { c++; } var valor = function() { return c; } return { somar: somar, valor: valor }; } var contador = criarContador(); console.log(contador.valor()); //Imprime 0 contador.somar() console.log(contador.valor()); //Imprime 1
No código acima, a variável “c”, dentro de “contador”, fica oculta, sendo acessível apenas através do binding.
Como Javascript é “pseudo-orientada a objetos”, certas coisas podem ser feitas como:
var c = { nome: "Me", getNome: function() { return this.nome; } } //this aponta para o objeto atual.
Aliás, isso é algo para se comentar: a construção {}, no Javascript, cria um “Objeto”. É algo semelhante à linguagem Lua e as listas. Quando se define uma função como um dos membros do objeto (vou chamar de “membro” ao invés de valor, para evitar confundir com hashes) a variável especial “this” aponta para o objeto (logo, a função getNome, acima, aponta para o objeto “c”). Entretanto, quando se faz isso:
var c = { nome: "Maurício", imprime: function() { var outraFuncao = function() { console.log(this.nome); }; outraFuncao(); } } c.imprime();
Ao contrário do esperado, isso retorna “undefined”. Porque, nesse caso, o this aponta para a função, não para o objeto (uma função declarada dentro de outra função, sem ser membra do objeto, tem esse comportamento). Para dar o resultado esperado, basta criar uma variável acessível pela closure:
var c = { nome: "Maurício", imprime: function() { var self = this; var outraFuncao = function() { console.log(self.nome); }; outraFuncao(); } } c.imprime();
Sobre variáveis, declarar uma variável fora de qualquer escopo cria uma variável global. Atribuir um valor a uma variável inexistente sem usar a palavra-chave “var” também cria uma variável global. Portanto, cuidado com tais construções. Caso seja necessário, em Javascript, rodar um código específico que tenha a necessidade de criar uma série de variáveis locais, englobá-las numa função, tal como:
(function () { var umaVariavel = 10; var outraVariavel = 20; //etc, etc... })();
E por fim, Javascript é uma linguagem orientada a “protótipos”. Todos os elementos possuem o membro especial “prototype”, que aponta para um objeto que é o protótipo. Basta encarar mais ou menos como os “mix-ins” do Ruby, mas diferente destes é possível selecionar quais métodos incluir. Como um exemplo, vamos adicionar um método a todas as Arrays:
Array.prototype.each = function(f) { for(var i = 0; i < this.length; i++) { f(this[i]); } }; [4, 5, 6].each( function(e) { console.log(e); }); //Imprime cada elemento
O código acima define um membro (each) para o protótipo (prototype) de todos os Array’s. Esse membro é uma função que itera nos elementos de uma forma mais simples do que o for.
Por sinal, o código acima me lembrou algo: se você testar o código removendo o “;” antes do “[4, 5, 6].each…”, no firebug o código não funciona. Javascript não dispensa os “;” no fim de cada comando. O que ele faz é TENTAR corrigir o código mal-formatado, mas a forma como ele faz isso depende da implementação, portanto é uma medida de segurança adicionar os pontos-e-vírgula.
Bom, essa é uma introdução. Nos próximos posts, tentarei trazer mais alguns conceitos legais à tona. Como JS é uma linguagem que foi expandida por muita gente, é difícil de saber o que falar, mas provavelmente os próximos assuntos serão sobre Unobtrusive Javascript, o assunto que todos falam e que é a preocupação mais nova do Rails 3.
2 Comments
Daniel · 2011-11-19 at 09:35
cara eu to buscando a respeito do node.js e tbm estou motivado a comprar um livro sobre javascript
Closures, Functions e Programação Funcional em Javascript | Maurício Szabo · 2010-11-01 at 17:20
[…] seguem a regra de “closures”, elas mantém o “binding” atual (ver o post estudos com Javascript). Portanto, se eu […]
Comments are closed.