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.