Monkey-Patch seguro, em Javascript

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.__defineGetter__('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.__defineGetter__('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.

Advertisements
This entry was posted in Javascript and tagged , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s