first capital javascript ecma

capital - ¿Por qué es legal `(foo)=“ bar ”` en JavaScript?



title case javascript (2)

En REPL de Node.js (probado también en SpiderMonkey) la secuencia

var foo = null; (foo) = "bar";

es válido, con foo posteriormente igual a "bar" en lugar de null .

Esto parece contrario a la intuición porque uno pensaría que el paréntesis al menos desreferiría la bar y arrojaría el lado izquierdo no válido en la asignación .

Comprensiblemente, cuando haces algo interesante, falla de la manera mencionada.

(foo, bar) = 4 (true ? bar : foo) = 4

De acuerdo con ECMA-262 en LeftHandExpressions (hasta donde puedo interpretar), no existen terminales no válidos que conduzcan a una aceptación entre paréntesis.

¿Hay algo que no esté viendo?


De hecho es válido. Se le permite envolver cualquier objetivo de asignación simple entre paréntesis.

La parte izquierda de la operación = es una LeftHandSideExpression como se identificó correctamente. Esto puede rastrearse a través de los diversos niveles de NewExpression ( NewExpression , MemberExpression ) a una PrimaryExpression , que a su vez podría ser una Cover­Parenthesized­Expression­And­Arrow­Parameter­List :

( Expression[In, ?Yield] )

(en realidad, cuando se analiza con PrimaryExpression destino, se trata de una PrimaryExpression ParenthesizedExpression ).

Así que es válido por la gramática, al menos. Si la sintaxis de JS es realmente válida está determinada por otro factor: la semántica estática de error temprano. Esas son básicamente reglas en prosa o algorítmicas que hacen que algunas expansiones de producción sean inválidas (errores de sintaxis) en ciertos casos. Esto, por ejemplo, permitió a los autores reutilizar las gramáticas de inicialización de matriz y objeto para la desestructuración, pero solo aplicando ciertas reglas. En los primeros errores de las expresiones de asignación encontramos

Es un Reference Error temprano si LeftHandSideExpression no es un ObjectLiteral ni un ArrayLiteral e IsValidSimpleAssignmentTarget de LeftHandSideExpression es false .

También podemos ver esta distinción en la evaluación de expresiones de asignación , donde los objetivos de asignación simples se evalúan como una referencia que se puede asignar, en lugar de obtener el patrón de desestructuración como objetos y literales de matriz.

Entonces, ¿qué hace que IsValidSimpleAssignmentTarget haga con LeftHandSideExpressions ? Básicamente, permite asignaciones a accesos de propiedades y no permite asignaciones a expresiones de llamada. No indica nada sobre las expresiones primarias simples que tienen su propia regla IsValidSimpleAssignmentTarget . Todo lo que hace es extraer la Expresión entre paréntesis a través de la operación CoveredParenthesizedExpression , y luego volver a verificar IsValidSimpleAssignmentTarget de eso. En resumen: (…) = … es válido cuando … = … es válido. Solo será true para Identifiers (como en su ejemplo) y propiedades.


Según la sugerencia de @dlatikay , siguiendo una corazonada, la investigación en CoveredParenthesizedExpression produjo una mejor comprensión de lo que está sucediendo aquí.

Aparentemente, la razón por la que no se puede encontrar un no terminal en la especificación , para explicar por qué (foo) es aceptable como una LeftHandExpression , es sorprendentemente simple. Supongo que usted comprende cómo funcionan los analizadores y que funcionan en dos etapas separadas: Lexing y Parsing .

Lo que he aprendido de esta pequeña investigación tangente es que la construcción (foo) no se está entregando técnicamente al analizador, y por lo tanto al motor, como podría pensarse.

Considera lo siguiente

var foo = (((bar)));

Como todos sabemos, algo como esto es perfectamente legal. ¿Por qué? Bueno, visualmente puedes ignorar el paréntesis, mientras que la afirmación permanece en perfecto sentido.

De manera similar, aquí hay otro ejemplo válido, incluso desde una perspectiva de legibilidad humana, porque los paréntesis solo PEMDAS lo que PEMDAS ya hace implícito.

(3 + ((4 * 5) / 2)) === 3 + 4 * 5 / 2 >> true

Una observación clave puede derivarse libremente de esto, dada la comprensión de cómo funcionan los analizadores. (recuerde, Javascript aún se está analizando ( lea: compilado ) y luego ejecute) Así que en un sentido directo, estos paréntesis están "indicando lo obvio" .

Entonces, todo lo que se dice, ¿qué está pasando exactamente?

Básicamente, los paréntesis (con la excepción de los parámetros de la función) se contraen en agrupaciones adecuadas de sus símbolos que contienen. IANAL, pero, en términos legos, significa que los paréntesis solo se interpretan para guiar al analizador sobre cómo agrupar lo que lee. Si el contexto de los paréntesis ya está "en orden", y por lo tanto no requiere ningún ajuste del AST emitido, entonces el código (máquina) se emite como si esos paréntesis no existieran en absoluto.

El analizador es más o menos perezoso, asumiendo que los parens son impertinentes. (que en este caso de borde, no es cierto)

De acuerdo, ¿y dónde exactamente está sucediendo esto?

De acuerdo con 12.2.1.5 Semántica estática: IsValidSimpleAssignmentTarget en la especificación,

PrimaryExpression: (CoverParenthesizedExpressionAndArrowParameterList)

  1. Deje que expr sea CoveredParenthesizedExpression de CoverParenthesizedExpressionAndArrowParameterList.
  2. Devuelve IsValidSimpleAssignmentTarget de expr.

IE Si se espera la primaryExpression devuelva lo que esté dentro del paréntesis y primaryExpression .

Por ello, en este escenario, no convierte (foo) en CoveredParenthesizedExpression{ inner: "foo" } , lo convierte simplemente en foo que conserva el hecho de que es un Identifier y, por lo tanto, sintácticamente, aunque no necesariamente léxicamente , válido. .

TL; DR

Es wat

¿Quieres un poco más de información?

Echa un vistazo a la respuesta de @Gergi .