Continuando os estudos com Javascript, diversas coisas interessantes, que pareciam acessíveis apenas aos programadores de Ruby, Perl e outras linguagens mais flexíveis podem ser feitas também na linguagem. O problema é que, normalmente, é difícil de usar isso em bibliotecas que já existem, principalmente porque a maioria dos códigos em Javascript ou não seguem boas-práticas ou simplesmente são escritos de forma diferente do que vou apresentar aqui, porém vale pela dica. Um exemplo bem simples, pensando na seguinte API:
objeto = {
valor: 10,
incrementar: function(v) {
if(!v) v = 1;
valor += v;
},
raizQuadrada: function() {
Math.sqrt(incrementar(4));
}
}
Para implementá-la, em Javascript, há várias formas. Simulando o comportamento que eu fiz no post passado, é possível usar o seguinte formato:
function createObjeto() {
var valor = 10;
var getValor = function() { return valor; }
var incrementar = function(v) {
if(!v) v = 1;
valor += v;
return valor;
};
var raizQuadrada = function() { return Math.sqrt(incrementar(6)); };
var O = new Function();
O.prototype.<strong>defineGetter</strong>('valor', getValor);
O.prototype.incrementar = incrementar;
O.prototype.raizQuadrada = raizQuadrada;
return new O();
}
Ok, agora vamos ver uma coisa realmente interessante: digamos que, por algum motivo estranho, alguém reimplemente o método “incrementar”. Monkey-patch total. Em Ruby, por exemplo, se reimplementarmos o atributo “valor” para ele retornar uma string, a função “raizQuadrada” simplesmente deixa de funcionar. Já em Javascript:
var objeto = createObjeto();
objeto.<strong>defineGetter</strong>('valor', function() {
return 'algo';
});
objeto.raizQuadrada(); //retorna 4 (sqrt(10 + 6)).
Ok, como ele retornou “4”, sendo que eu mudei o “getter” do object? O segredo está na linha:
incrementar: function(v) {
if(!v) v = 1;
valor += v; //<-- Aqui é o segredo.
},
Por quê? Porque estamos acessando a variável “valor” direto, sem usar o “getter”. Ah, então, se fizermos “monkey-patch” do “incrementar”, as coisas falham?
var objeto = createObjeto();
objeto.incrementar = function(v) {
if(!v) v = 1;
valor += v;
return valor;
};
objeto.raizQuadrada(); //retorna 4, de novo.
Por quê? O segredo, novamente, está na linha que chama a função “incrementar”, da função raizQuadrada. Porque, na verdade, estamos chamando a função que está na variável “incrementar” definida no “createObject”, não a função “incrementar” definda dentro do “objeto”. Isto, como é uma closure, mantém os bindings da função “createObjeto”. Logo, é uma maneira de fazer um “monkey-patch” seguro, em Javascript, aproveitando-se do fato de que as funções (function) em Javascript criam, na verdade, closures e dessa forma mantém o binding anterior.