Tableless

Busca Menu

Javascript Observe

Seja o primeiro a comentar por

O ECMAScript 6 (codinome Harmony ou ES.next) já não é novidade para quem acompanha de perto os avanços do desenvolvimento web, e suas especificações já bem avançadas causam um certo alvoroço a respeito de como vais ser o futuro do Javascript.

Uma das esperadas especificações é o Object.observe, que documenta a capacidade de observar e notificar aplicações sobre as mudanças ocorridas em objetos Javascript, recurso bastante requisitado em aplicações funcionais.

O uso de funcionalidades similares já é possível com algumas bibliotecas como o Watch.js e também por frameworks como o Backbone (além de outros), permitindo a criação de mecanismos declarativos e independentes das demais implementações da aplicação.

Para começar, precisamos entender o que esta funcionalidade é capaz de observar um objeto:

  • As mudanças no valor das propriedades;
  • A adição, exclusão e reconfiguração de todas as propriedades visíveis através das APIs de reflexão;
  • Mudanças no protótipo e extensibilidade do próprio objeto.

A API pública atual disponibiliza as seguintes métodos:

 Object.observe(Object, callback[, accepts])

Trata-se do método principal da API que é responsável pela funcionalidade de observar um objeto e sua assinatura é bastante simples, sendo necessário apenas informar o objeto a ser observado, a função a ser executada quando alguma notificação for disparada e opcionalmente um vetor de tipos de notificações a serem manipuladas (os tipos de notificações permitidas são: ”add”, “update”, “delete”, “reconfigure”, “setPrototype” e “preventExtensions”); Esta chamada pode ser executada quantas vezes for necessário (você pode querer executar callbacks distintos).

Sua utilização segue o seguinte exemplo:

function observer(regs) { 
   console.log(regs); 
} 
var obj = { id: 1 }; 
Object.observe(obj, observer); 
obj.a = 'b'; 
obj.id++;

A saída desta rotina, desconsiderando as propriedades herdadas pelo argumento regs, seria uma lista de objetos de notificações:

> [{
     name: "a",
     object: {id: 2, a: "b"},
     oldValue: "b", 
     type: "updated" 
  },{ 
     name: "id", 
     object: {id: 2, a: "b"}, 
     type: "new"
  }]

Object.unobserve(Object, callback)

Eventualmente, poderá ser necessário não acompanhar mais as notificações de um determinado objeto, ou apenas interromper a execução de um callback especifico; Para isso, basta executar conforme o exemplo, especificando o objeto observado e o callback a ser removido:

function updateObserver(regs) { 
   regs.forEach(function(notification){
      if(notification.type === "updated"){
         console.log(notification);
      }
   });
}
function deleteObserver(regs) { 
   regs.forEach(function(notification){
      if(notification.type === "deleted"){
         console.log(notification);
      }
   });
} 
var obj = { id: 1 }; 
Object.observe(obj, updateObserver); 
Object.observe(obj, deleteObserver); 
obj.id++; 
Object.unobserve(obj, deleteObserver); 
delete obj.id;

A saída desta rotina, também seria uma lista de objetos de notificações:

> [{
     name: "id",
     object: {},
     oldValue: 1, 
     type: "updated" 
  }]

Array.observe(Object, callback)

É apenas um atalho, equivalente a:

function(obj, callback) {
    return Object.observe(obj, callback, ["add", "update", "delete", "splice"]);
}

Array.unobserve(Object, callback)

Outro atalho, equivalente a:

function(obj, callback) {
    return Object.unobserve(obj, callback);
}

Object.deliverChangeRecords

Se notarmos o primeiro exemplo utilizado no método Object.observe, é possível perceber que a lista de notificações foi entregue de forma acumulada em um vetor, ou seja, no final de todas as alterações. Caso seja necessário intervir de forma antecipada o método Object.deliverChangeRecords garante uma chamada imediata do callback especificado (necessita ser um callback já aplicado) com uma lista das notificações pendentes:

function observer(regs) { 
   console.log(regs); 
} 
var obj = { id: 1 }; 
Object.observe(obj, observer); 
obj.a = 'b'; 
obj.id++;
console.log('Primeira chamada');
Object.deliverChangeRecords(observer);
obj.b = 'c'; 
console.log('Segunda chamada');
Object.deliverChangeRecords(observer);

Esta execução revelaria duas pilhas de notificações:

> Primeira chamada
> [{
     name: "a",
     object: { a: "b", b: "c", id: 2 },
     type: "new"
  },{
     name: "id"
     object: { a: "b", b: "c", id: 2 }
     oldValue: 1
     type: "updated"
  }]
> Segunda chamada
> [{
     name: "b",
     object: {a: "b", b: "c"},
     id: 2,
     type: "new"
  }]

Object.getNotifier

Permite recuperar o objeto notificador e forçar uma notificação através do método notify():

var obj = { id: 1 };
function basicObserver(changeList){
   changeList.forEach(function(change){
      console.log(change);
   });
}
Object.observe(obj,basicObserver); 
Object.getNotifier(obj).notify({ type: "new", name: "id" });

É importante observarmos que implementações nativas tendem a ter um desempenho superior às implementações customizadas (ex. frameworks), e considerando este como apenas um dos aspectos das futuras implementações do ECMAScript 6 já podemos afirmar que elas representam um passo importante para a maturidade do desenvolvimento web.

Caso você não queira aguardar sentado o Javascript Harmony existem algumas alternativas como o Object.observe (polyfill/shim) ou a utilização do Chrome Canary com a flag “Enable Experimental JS APIs” ativada.

Publicado no dia