Perché JavaScript eval ha bisogno di parentesi per evalare i dati JSON?

Ho imparato (nel modo più duro) che ho bisogno di aggiungere parentesi sui dati JSON, come questo:

stuff = eval('(' + data_from_the_wire + ')'); // where data_from_the_wire was, for example {"text": "hello"} 

(In Firefox 3, almeno).

Qual è la ragione di questo? Odio scrivere codice senza capire cosa c’è dietro il cofano.

Mettere le parentesi attorno a data_from_the_wire è effettivamente equivalente a

 stuff = eval('return ' + data_from_the_wire + ';'); 

Se dovessi valutare senza le parentesi, allora il codice verrebbe valutato, e se tu avessi al suo interno delle funzioni con nome quelle sarebbero definite, ma non restituite.

Prendiamo ad esempio la possibilità di chiamare una funzione così come è stata creata:

 (function() { alert('whoot'); })() 

Chiamerà la funzione che è stata appena definita. Il seguente, tuttavia, non funziona:

 function() { alert('whoot'); }() 

Quindi vediamo che le parentesi trasformano efficacemente il codice in un’espressione che restituisce, piuttosto che solo il codice da eseguire.

eval accetta una sequenza di istruzioni Javascript. Il parser Javascript interpreta il token ‘{‘, che si verifica all’interno di un’istruzione come l’inizio di un blocco e non l’inizio di un object letterale.

Quando si racchiude il letterale in parentesi come segue: ({ data_from_the_wire }) si sta commutando il parser Javascript in modalità di analisi delle espressioni. Il token ‘{‘ all’interno di un’espressione significa l’inizio di una dichiarazione letterale dell’object e non di un blocco, e quindi Javascript lo accetta come object letterale.

Non sono sicuro del motivo, ma analizzo JSON utilizzando la class JSON di json.org . È molto più sicuro che usare eval.

In JavaScript, le parentesi graffe vengono utilizzate per creare istruzioni di blocco:

 { var foo = "bar"; var blah = "baz"; doSomething(); } 

Le righe precedenti possono essere inserite in una stringa e valutate senza problemi. Ora considera questo:

 { "foo": "bar", "blah": "baz" } 

Le parentesi graffe fanno sì che il motore JavaScript pensi che sia un’espressione di gruppo, quindi l’errore di syntax attorno al carattere : Citazione da MDN … Guida JavaScript … Letterali object :

Non si dovrebbe usare un object letterale all’inizio di un’istruzione. Ciò causerà un errore o non si comporterà come ci si aspetta, poiché {sarà interpretato come l’inizio di un blocco.

La soluzione alternativa di avvolgere l’object literal inside () funziona dicendo al motore di trattare i suoi contenuti come un’espressione, non come un’istruzione di blocco. Quindi questo non funziona:

 ({ var foo = "bar"; var blah = "baz"; doSomething(evil); }) // parse error 

Ma questo:

 ({ "foo": "bar", "blah": "baz" }) // returns object 

Questo accade perché senza le parentesi tonde JavaScript cerca di interpretare {"text": ... come etichetta e fallisce. Provalo in console e riceverai l’errore “etichetta non valida”.

Dipende dal valore di data_from_the_wire , in realtà. Nella maggior parte dei casi la syntax è corretta, ma una riga che inizia con { viene analizzata come etichetta e la tua non è valida. Se lo si circonda con una parentesi, impedisce al parser di interpretare erroneamente la propria espressione.

Solo un problema di analisi, davvero. Con stringhe, numeri o funzioni, non avresti quel problema.

Una soluzione è di valutare sempre le istruzioni e non le espressioni. Tu puoi scrivere

 eval('var stuff = {"text": "hello"}'); 

Non lo so, e in realtà sono interessato alla risposta a questo, ma la mia ipotesi è che senza le parentesi i dati in data_from_the_wire sarebbero interpretati come una chiusura. Forse la parentesi forza la valutazione e quindi l’array associativo viene “restituito”.

Questo è il tipo di ipotesi che porta a downvotes però =).

MODIFICARE

Douglas Crockford menziona un’ambiguità di syntax sul suo sito JSON ma questo non mi è stato di grande aiuto.