Dado um objeto javascript, é possível varrer seus métodos e propriedades e até mesmo serializá-lo.
Para isso devemos fazer uma conversão forçada do objeto para um vetor, e varre-lo. Cada item desse vetor pode ser um método ou uma propriedade.
O que vai dizer se ele é um método ou uma propriedade é que seu tipo, dado pela função typeof():
- quando, como string, for igual a "function" significa que ele é um método ou função.
- caso contrário é uma propriedade.
Código da função de serialização
///Transforma um objeto em string considerandp se é null ou undefined
function ObjToString(obj)
{
if(obj === null)
{
return "null";
}
if(typeof obj === "undefined")
{
return "undefined";
}
return obj.toString();
}
///escreve as propriedades do objeto na página
function serialize(obj)
{
//coleção de métodos
var metodos = [];
//coleção de propriedades
var propriedades = [];
//varre as propriedades
for (i in obj)
{
//verifica se não é a própria função
if (i != "serialize")
{
if (typeof(obj[i]) == "function")
{
//se for método adiciona em uma coleção
metodos.push(typeof(obj[i]) + ": " + i + ": " + ObjToString(obj[i]) );
}
else
{
//senão adiciona na outra
propriedades.push(typeof(obj[i]) + ": " + i + ": " + ObjToString(obj[i]) );
}
}
}
//varre as coleções escrevendo na página
document.write("metodos
");
for (m in metodos)
{
document.write(metodos[m]+"
");
}
document.write("
propriedades
");
for (p in propriedades)
{
document.write(propriedades[p]+"
");
}
}
No caso dessa função, ela simplesmente lista as propriedades e métodos e os concatena a seus devidos valores, pulando linha.
Isso poderia ser feito de outra forma, por exemplo, a função de serialização poderia gerar um JSON e usá-lo em uma requisição ajax,
ou interpretá-lo com eval() e gerar um clone do objeto em questão (apenas as propriedades, um DTO), ou ainda serializá-lo em XML.
Por enquanto vamos listar os métodos e propriedades na página mesmo. Depois tentamos gravar em uma string ou objeto json.
Note o uso do comando for ... in. for (i in obj)
Ele equivale ao foreach do c# e de outras linguagens. Ele varre todos os elementos de um array atribuindo a I o elemento do array de indice x quando for um array, ou a propriedade quando for um ojeto (como se fosse um hashtable/dictionay).
Usando esse comando, para uma varredura geral sem armazenar ou utilizar o índice, você pode fazer uma varredura geral em um array sem se preocupar com os limites inferior e superior.
A função ObjToString apenas verifica se o objeto é nulo ou undefined antes de chamar .toString() nele.
Abaixo um exemplo de serialização do objeto window:
serialize(window);
Com a saída:
metodos
function: postMessage: function () { [native code] }
function: close: function () { [native code] }
function: blur: function () { [native code] }
function: focus: function () { [native code] }
function: getSelection: function getSelection() { [native code] }
function: print: function print() { [native code] }
function: stop: function stop() { [native code] }
function: open: function open() { [native code] }
function: alert: function alert() { [native code] }
function: confirm: function confirm() { [native code] }
function: prompt: function prompt() { [native code] }
function: find: function find() { [native code] }
function: scrollBy: function scrollBy() { [native code] }
function: scrollTo: function scrollTo() { [native code] }
function: scroll: function scroll() { [native code] }
function: moveBy: function moveBy() { [native code] }
function: moveTo: function moveTo() { [native code] }
function: resizeBy: function resizeBy() { [native code] }
function: resizeTo: function resizeTo() { [native code] }
function: matchMedia: function matchMedia() { [native code] }
function: getComputedStyle: function getComputedStyle() { [native code] }
function: getMatchedCSSRules: function getMatchedCSSRules() { [native code] }
function: webkitConvertPointFromPageToNode: function webkitConvertPointFromPageToNode() { [native code] }
function: webkitConvertPointFromNodeToPage: function webkitConvertPointFromNodeToPage() { [native code] }
function: requestAnimationFrame: function requestAnimationFrame() { [native code] }
function: cancelAnimationFrame: function cancelAnimationFrame() { [native code] }
function: webkitRequestAnimationFrame: function webkitRequestAnimationFrame() { [native code] }
function: webkitCancelAnimationFrame: function webkitCancelAnimationFrame() { [native code] }
function: webkitCancelRequestAnimationFrame: function webkitCancelRequestAnimationFrame() { [native code] }
function: captureEvents: function captureEvents() { [native code] }
function: releaseEvents: function releaseEvents() { [native code] }
function: btoa: function btoa() { [native code] }
function: atob: function atob() { [native code] }
function: setTimeout: function setTimeout() { [native code] }
function: clearTimeout: function clearTimeout() { [native code] }
function: setInterval: function setInterval() { [native code] }
function: clearInterval: function clearInterval() { [native code] }
function: webkitRequestFileSystem: function webkitRequestFileSystem() { [native code] }
function: webkitResolveLocalFileSystemURL: function webkitResolveLocalFileSystemURL() { [native code] }
function: openDatabase: function openDatabase() { [native code] }
function: addEventListener: function addEventListener() { [native code] }
function: removeEventListener: function removeEventListener() { [native code] }
function: dispatchEvent: function dispatchEvent() { [native code] }
propriedades
object: top
object: window
object: location
object: external
object: chrome
object: document
object: speechSynthesis
object: localStorage
object: sessionStorage
object: applicationCache
object: webkitStorageInfo
object: indexedDB
object: webkitIndexedDB
object: crypto
object: CSS
object: performance
object: console
number: devicePixelRatio
object: styleMedia
object: parent
object: opener
object: frames
object: self
string: defaultstatus
string: defaultStatus
string: status
string: name
number: length
boolean: closed
number: pageYOffset
number: pageXOffset
number: scrollY
number: scrollX
number: screenTop
number: screenLeft
number: screenY
number: screenX
number: innerWidth
number: innerHeight
number: outerWidth
number: outerHeight
boolean: offscreenBuffering
object: frameElement
object: clientInformation
object: navigator
object: toolbar
object: statusbar
object: scrollbars
object: personalbar
object: menubar
object: locationbar
object: history
object: screen
object: onautocompleteerror
object: onautocomplete
object: ondeviceorientation
object: ondevicemotion
object: onunload
object: onstorage
object: onpopstate
object: onpageshow
object: onpagehide
object: ononline
object: onoffline
object: onmessage
object: onlanguagechange
object: onhashchange
object: onbeforeunload
object: onwaiting
object: onvolumechange
object: ontoggle
object: ontimeupdate
object: onsuspend
object: onsubmit
object: onstalled
object: onshow
object: onselect
object: onseeking
object: onseeked
object: onscroll
object: onresize
object: onreset
object: onratechange
object: onprogress
object: onplaying
object: onplay
object: onpause
object: onmousewheel
object: onmouseup
object: onmouseover
object: onmouseout
object: onmousemove
object: onmouseleave
object: onmouseenter
object: onmousedown
object: onloadstart
object: onloadedmetadata
object: onloadeddata
object: onload
object: onkeyup
object: onkeypress
object: onkeydown
object: oninvalid
object: oninput
object: onfocus
object: onerror
object: onended
object: onemptied
object: ondurationchange
object: ondrop
object: ondragstart
object: ondragover
object: ondragleave
object: ondragenter
object: ondragend
object: ondrag
object: ondblclick
object: oncuechange
object: oncontextmenu
object: onclose
object: onclick
object: onchange
object: oncanplaythrough
object: oncanplay
object: oncancel
object: onblur
object: onabort
object: onwheel
object: onwebkittransitionend
object: onwebkitanimationstart
object: onwebkitanimationiteration
object: onwebkitanimationend
object: ontransitionend
object: onsearch
number: TEMPORARY
number: PERSISTENT
Agora imagine um objeto json, por exemplo pessoa:
var pessoa = {
nome:"Vitor",
sobrenome:"Rubio",
email:"vitorrubio@gmail.com",
idade:30,
dataCadastro:"14/10/2014",
dependente:null,
hobby:undefined};
Com a saída:
metodos
propriedades
string: nome: Vitor
string: sobrenome: Rubio
string: email: vitorrubio@gmail.com
number: idade: 30
string: dataCadastro: 14/10/2014
object: dependente: null
undefined: hobby: undefined
Repare também que foi necessário colocar uma excessão quanto ao método "serialize" para ele não serializar a si mesmo.
Se um método for nativo do browser ele é serializado como [native code], mas se for um método seu então todo o source do método é serializado.
Veja o poder disso: nas linguagens compiladas como C ou Delphi, ou nas linguagens com compilação Just In Time como C# e Java, os objetos podem ter suas propriedades e valores varridos, serializando-os para xml, json e outros formatos. Mas apenas seus campos e propriedades. NUNCA SEUS MÉTODOS.
Então nas aplicações, não importa se você usa um banco de dados relacional ou orientado a objeto, você nunca poderia armazenar o código executável (métodos) no banco de dados. Apenas os dados poderiam ser armazenados. Isso significaria ter que tratar os dados com functions e stored procedures no banco de dados ou reconstituir o objeto e tratar na aplicação.
Com javascript é diferente. Você pode salvar o SOURCE de métodos e argumentos em um banco de dados, trazêlos de volta a vida com eval(), JSON.parse ou jQuery.parseJSON e executar as mesmas funções que eles já possuiam.
Não é possível fazer isso com JSON.parse(), apenas com eval, pois entende-se que o JSON é apenas para transferência de DADOS. Mesmo assim você pode embutir o eval como mostrado
nesse link
O método abaixo faz o mesmo que o serialize, mas joga o resultado em uma string.
///o mesmo que serialize só que retornando uma string
function SerializeObjToString(obj)
{
var metodos = [];
var propriedades = [];
var resultado = "";
for (i in obj)
{
//tirada a restrição do "serialize". Deixa trazer tudo.
if (typeof(obj[i]) == "function")
{
metodos.push(typeof(obj[i]) + ": " + i + ": " + ObjToString(obj[i]) );
}
else
{
propriedades.push(typeof(obj[i]) + ": " + i + ": " + ObjToString(obj[i]) );
}
}
//a string já é formatada
resultado+="metodos
";
for (m in metodos)
{
resultado+=metodos[m]+"
";
}
resultado+="
propriedades
";
for (p in propriedades)
{
resultado+=propriedades[p]+"
";
}
return resultado;
}
O método abaixo transforma o objeto javascript em uma string JSON.
///gera uma string json para ser interpretada com eval ou json.parse
///não é segura quanto à recursividade, referência cíclica
function SerializeObjToJSON(obj)
{
var metodos = [];
var propriedades = [];
var resultado = "";
for (i in obj)
{
//tirada a restrição do "serialize". Deixa trazer tudo.
//verifica primeiro se o objeto é undefined, para retornar undefined
if(typeof (obj[i]) === "undefined")
{
metodos.push("\""+i+"\"" + ":" + "\"undefined\"" );
}
//se for uma function retorna o corpo da mesma com ObjToString sem aspas
else if (typeof(obj[i]) == "function")
{
metodos.push("\""+i+"\"" + ":" + ObjToString(obj[i]) );
}
//se for um número usa ObjToString para retornar sem aspas
else if (typeof(obj[i]) == "number")
{
metodos.push("\""+i+"\"" + ":" + ObjToString(obj[i]) );
}
//se for objeto verifica se é nulo ou não
else if (typeof(obj[i]) == "object")
{
//se for nulo simplesmente concatena null
if(obj[i] === null)
{
metodos.push("\""+i+"\"" + ":" + "\"null\"" );
}
//senão chama a propria função e serializa recursivamente
else
{
metodos.push("\""+i+"\"" + ":" + SerializeObjToJSON(obj[i]) );
}
}
else
{
//senão assume que é string e poe entre aspas. Se tiver errado é fácil alterar.
propriedades.push("\""+i+"\"" + ":\"" + ObjToString(obj[i])+"\"" );
}
}
//começa e termina entre chaves
resultado+="{";
for (var m = 0; m 0)
{
resultado+=",";
}
}
else
{
resultado+=",";
}
resultado+="\r\n";
}
for (var p = 0; p < propriedades.length; p++)
{
resultado+=propriedades[p];
if(p < propriedades.length-1)
{
resultado+=",";
}
resultado+="\r\n";
}
resultado+="}";
return resultado;
}
Que pode ser transformada de volta em objeto da seguinte maneira.
function evalJson()
{ try
{
//poe a string entre parenteses antes do eval
var txt = SerializeObjToJSON(pessoa);
alert(txt);
//var obj = eval("(" + txt + ")"); //para usar eval
var obj = JSON.parse(txt); //para usar JSON.parse nativo
//var obj = JSON && JSON.parse(txt) || $.parseJSON(txt); //para decidir de acordo com o browser se vai usar JSON nativo ou a versão do jQuery
alert(obj.email);
}
catch(e)
{
alert(e.message);
}
}
Bom, por enquanto é isso. Acredito que esse é o meu maior post sobre javascript. Pretendo postar amanhã uma galeria com jQuery e dar uma organizada nesse blog.
Baixar o Exemplo
Have fun :)
Comentários
Postar um comentário