Por que o eval do JavaScript precisa de parênteses para avaliar os dados JSON?

Eu aprendi (da maneira mais difícil) que eu preciso adicionar parênteses em torno de dados JSON, como este:

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

(No Firefox 3, pelo menos).

Qual é a razão por trás disso? Eu odeio escrever código sem entender o que está por trás do capô.

Colocar os parênteses em torno de data_from_the_wire é efetivamente equivalente a

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

Se você fosse eval sem os parênteses, então o código seria avaliado, e se você tivesse quaisquer funções nomeadas dentro delas, elas seriam definidas, mas não retornadas.

Tome como exemplo a capacidade de chamar uma function da mesma forma que foi criada:

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

Vai chamar a function que acaba de ser definida. O seguinte, no entanto, não funciona:

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

Assim, vemos que os parênteses efetivamente transformam o código em uma expressão que retorna, em vez de codificar para ser executada.

eval aceita uma sequência de instruções JavaScript. O analisador de JavaScript interpreta o token ‘{‘, ocorrendo em uma instrução como o início de um bloco e não o início de um literal de object.

Quando você coloca seu literal entre parênteses como este: ({ data_from_the_wire }) você está trocando o analisador de Javascript para o modo de análise de expressão. O token ‘{‘ dentro de uma expressão significa o início de uma declaração literal do object e não um bloco e, portanto, o JavaScript a aceita como um literal de object.

Não tenho certeza do motivo, mas analiso o JSON usando a class JSON do json.org . É muito mais seguro que usar o eval.

Em JavaScript, chaves são usadas para criar instruções de bloco:

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

As linhas acima podem ser colocadas dentro de uma string e avaliadas sem problemas. Agora considere isto:

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

As chaves fazem com que o mecanismo JavaScript pense que é uma expressão de grupo, daí o erro de syntax em torno do caractere : Citação do MDN … JavaScript Guide … Literais de Objetos :

Você não deve usar um literal de object no início de uma instrução. Isso levará a um erro ou não se comportará como esperado, porque {será interpretado como o início de um bloco.

A solução alternativa de encapsular o literal de object dentro de () funciona dizendo ao mecanismo para tratar seu conteúdo como uma expressão, não como uma instrução de bloco. Então isso não funciona:

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

Mas isso faz:

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

Isso acontece porque, sem chaves, o JavaScript tenta interpretar {"text": ... como um label e falha. Experimente no console e você receberá um erro de “label inválido”.

Depende do valor de data_from_the_wire , na verdade. Na maioria dos casos, sua syntax é boa, mas uma linha que começa com { é analisada como um label e a sua é inválida. Se você cercar com parênteses, isso impedirá que o analisador interprete erroneamente sua expressão.

Apenas um problema de análise, na verdade. Com strings, números ou funções, você não teria esse problema.

Uma solução é sempre avaliar as instruções e não as expressões. Você pode escrever

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

Eu não sei, e estou realmente interessado na resposta para isso, mas meu palpite é que sem os parênteses os dados em data_from_the_wire seriam interpretados como um fechamento. Talvez os parênteses force a avaliação e, portanto, a matriz associativa seja “retornada”.

Este é o tipo de suposição que leva a votos negativos =).

EDITAR

Douglas Crockford menciona uma ambiguidade de syntax em seu site JSON, mas isso realmente não me ajudou.