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):
1 2 3 4 5 | 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:
1 2 3 4 5 6 7 | //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:
1 | var funcao = function (e) { console.log(e); } |
E, por fim, funções podem ser passadas entre si:
1 2 3 4 5 6 7 8 9 10 | 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | //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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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:
1 | 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:
1 2 3 4 5 6 7 8 9 10 | 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:
1 2 3 4 5 6 7 8 9 10 11 | 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:
1 2 3 4 5 | ( 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:
1 2 3 4 5 6 7 | 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.