2012-07-31 14 views
5

Vorrei aggiungere coppie di valori-chiave di metadati a oggetti JavaScript arbitrari. Questi metadati non dovrebbe influenzare il codice che non è a conoscenza dei metadati, che significa, ad esempioC'è un modo per aggiungere meta-dati agli oggetti JavaScript?

JSON.stringify(obj) === JSON.stringify(obj.WithMetaData('key', 'value')) 

metadati codice a conoscenza dovrebbe essere in grado di recuperare i dati con chiave, vale a dire

obj.WithMetaData('key', 'value').GetMetaData('key') === 'value' 

non v'è alcun modo per farlo - in node.js? In tal caso, funziona con tipi predefiniti come String e anche Number ? (Modifica Pensandoci, non mi interessa il numero reale numeri primitivi, ma avere quello per le istanze stringa sarebbe bello).

Alcuni Background: Quello che sto cercando di fare è di cache valori che sono derivati ​​da un oggetto con l'oggetto stesso, in modo che

  • per i meta dati del codice ignari, i metadati oggetto arricchito guarderà la stessa come l'oggetto originale w/o meta codice
  • che ha bisogno di valori derivati ​​possono ottenere fuori dei meta-dati se già memorizzati nella cache
  • cache otterrà garbage collection a fianco l'oggetto

Un altro modo sarebbe di memorizzare una tabella hash con le cache da qualche parte, ma non si saprebbe mai quando l'oggetto viene raccolto. Ogni istanza di oggetto dovrebbe essere gestita manualmente, in modo che le cache non perdano.

(btw clojure ha questa caratteristica: http://clojure.org/metadata)

+0

Questa suoni mi piace la funzionalità 'jQuery.data()' per me –

risposta

7

È possibile utilizzare la nuova API delle proprietà dell'oggetto ECMA5 per archiviare le proprietà sugli oggetti che non verranno visualizzati nell'enumerazione ma che sono comunque recuperabili.

var myObj = {}; 
myObj.real_property = 'hello'; 
Object.defineProperty(myObj, 'meta_property', {value: 'some meta value'}); 
for (var i in myObj) 
    alert(i+' = '+myObj[i]); //only one property - @real_property 
alert(myObj.meta_property); //"some meta value" 

Maggiori informazioni qui: link

Tuttavia non sta andando ad essere in grado di fare questo su tipi primitivi come le stringhe o numeri, solo su tipi complessi.

[EDIT]

Un altro approccio potrebbe essere quello di utilizzare il prototipo di un tipo di dati per memorizzare i metadati. (Attenzione, hack avanti). Quindi per stringhe:

String.prototype.meta = {}; 
String.prototype.addMeta = function(name, val) { this.meta[name] = val; } 
String.prototype.getMeta = function(name) { return this.meta[name]; }; 
var str = 'some string value'; 
str.addMeta('meta', 'val'); 
alert(str.getMeta('meta')); 

Tuttavia, questo non è chiaramente l'ideale. Per prima cosa, se la stringa è stata raccolta o alterata (poiché i tipi di dati semplici sono copiati per valore, non per riferimento) si perderebbe questa meta. Solo il primo approccio ha un chilometraggio in un ambiente reale, per essere onesti.

+0

Questo è bizzarro e intrigante. La non enumerabilità è il comportamento specificato o solo una conseguenza del supporto del browser? – Beejamin

+1

No in base alla progettazione, parte della nuova specifica delle proprietà dell'oggetto in ECMA5. È possibile stabilire che le proprietà * dovrebbero * essere enumerabili, ma di default non lo sono. Ho fatto [un ampio blog su questo] (http://www.mitya.co.uk/blog/2012/Feb/ECMAScript-revolution-object-properties-201) qualche tempo fa che potrebbe aiutare. – Utkanos

+0

Grande - grazie per i chiarimenti. Ho imparato qualcosa di nuovo oggi! – Beejamin

0

Non v'è alcun sistema di "commento" in JSON. Il meglio che puoi sperare è aggiungere una proprietà con un nome improbabile e aggiungere quella chiave che contenga i metadati. Puoi quindi leggere nuovamente i metadati se sai che sono metadati, ma altre configurazioni la vedranno semplicemente come un'altra proprietà. E se qualcuno usa for..in ...

+0

-1 Vedi risposta di * Utkanos * –

1

Si potrebbe semplicemente aggiungere i metadati come variabile "privata" !?

var Obj = function (meta) { 
    var meta = meta; 
    this.getMetaData = function (key) { 
     //do something with the meta object 
     return meta; 
    }; 
}; 
var ins_ob = new Obj({meta:'meta'}); 
var ins_ob2 = new Obj(); 
if(JSON.stringify(ins_ob) === JSON.stringify(ins_ob2)) { 
    console.log('hoorai'); 
}; 
+0

Qualcosa come "aggiungere una chiusura che si chiude su una variabile privata in un oggetto". Il problema è che la funzione 'getMetaData' è enumerabile e sebbene' JSON.stringify' non la serializzi, si presenterà ad esempio in 'for (var k in ins_ob)'. –

4

La specifica ES6 introduce Map e WeakMap. Puoi abilitarli nel nodo eseguendo node --harmony e abilitando il flag javascript sperimentale in Chrome, (è anche in Firefox per impostazione predefinita). Mappe e WeakMaps consentono di utilizzare oggetti come chiavi che possono essere utilizzati per memorizzare metadati su oggetti che non sono visibili a nessuno senza accesso alla mappa/mappa debole specifica. Questo è un modello io uso un sacco:

function createStorage(creator){ 
    creator = creator || Object.create.bind(null, null, {}); 
    var map = new Map; 
    return function storage(o, v){ 
    if (1 in arguments) { 
     map.set(o, v); 
    } else { 
     v = map.get(o); 
     if (v == null) { 
     v = creator(o); 
     map.set(o, v); 
     } 
    } 
    return v; 
    }; 
} 

utilizzo è semplice e potente:

var _ = createStorage(); 

_(someObject).meta= 'secret'; 
_(5).meta = [5]; 
var five = new Number(5); 
_(five).meta = 'five'; 

console.log(_(someObject).name); 
console.log(_(5).meta); 
console.log(_(five).meta); 

Facilita anche alcuni usi interessanti per la separazione implementazione dall'interfaccia:

var _ = createStorage(function(o){ return new Backing(o) }); 

function Backing(o){ 
    this.facade = o; 
} 
Backing.prototype.doesStuff = function(){ 
    return 'real value'; 
} 

function Facade(){ 
    _(this); 
} 
Facade.prototype.doSomething = function doSomething(){ 
    return _(this).doesStuff(); 
} 
+0

Cose interessanti. Grazie per avermi indirizzato a ES6, non sapevo della sua esistenza. Guardando la bozza attuale delle specifiche, le parole 'WeakMap' o anche solo' weak' non ci sono. Questo ci dice qualcosa sulle probabilità che WeakMaps sia incluso in ES6 o perché non è lì. –

+0

Anche questo può essere utilizzato solo con oggetti, non con stringhe. Quindi la principale differenza tra * Utkanos * 'suggerimento e 'WeakMap's sarebbe che WeakMaps ti permette di impedire ad altri di vedere e modificare i metadati. –

+0

Una WeakMap richiede che le chiavi siano oggetti ma una mappa no. Nel mio codice sopra è sufficiente cambiare WeakMap in Map e ottenere il risultato desiderato. Per quanto riguarda l'inclusione in ES6, sarà certamente, ma per qualche motivo non è ancora nelle specifiche. È già implementato in V8 e Spidermonkey, una delle prime funzionalità di ES6 da implementare. Puoi trovarlo su http://wiki.ecmascript.org/doku.php?id=harmony:proposals che elenca le proposte ES6 accettate. http://wiki.ecmascript.org/doku.php?id=harmony:simple_maps_and_sets http://wiki.ecmascript.org/doku.php?id = harmony: weak_maps –

Problemi correlati