Precedencia del operador con el operador ternario de Javascript

116

Parece que no puedo entender la primera parte de este código (+ =) en combinación con el operador ternario.

h.className += h.className ? ' error' : 'error'

La forma en que creo que funciona este código es la siguiente:

h.className = h.className + h.className ? ' error' : 'error'

Pero eso no es correcto porque da un error en mi consola.

Entonces, mi pregunta es ¿cómo debo interpretar este código correctamente?

Baijs
fuente

Respuestas:

141
h.className = h.className + (h.className ? ' error' : 'error')

Si desea que el operador trabaje h.className, será mejor que sea específico al respecto.
Por supuesto, ningún daño debería provenir de h.className += ' error' , pero ese es otro asunto.

Además, tenga en cuenta que +tiene prioridad sobre el operador ternario: Precedencia del operador de JavaScript

Kobi
fuente
3
Creo que debe tenerse en cuenta que, si bien no debería haber ningún daño h.className += ' error', también deja un espacio en blanco al comienzo de la cadena si inicialmente estaba vacío. Creo que el objetivo de la operación ternaria es producir una cuerda de aspecto limpio.
JMTyler
@JMTyler - Eso es exactamente lo que estaba indicando - Si todo está hecho solo para mantener un espacio desde el principio, no valgo la pena. (el caso de borde incluye selectores exactos de jQuery o XPath). Gracias de todos modos.
Kobi
¡@Kobi +1 solo para la advertencia de precedencia del operador!
Ed Chapel
129

Piénsalo de esta manera:

<variable> = <expression> ? <true clause> : <false clause>

La forma en que se ejecuta la declaración es básicamente la siguiente:

  1. ¿Se <expression>evalúa como verdadero o se evalúa como falso?
  2. Si se <expression>evalúa como verdadero, entonces <true clause>se asigna el valor de <variable>, <false clause>se ignora y se ejecuta la siguiente instrucción.
  3. Si se <expression>evalúa como falso, <true clause>se ignora y <false clause>se asigna el valor de <variable>.

Lo importante que hay que tener en cuenta con el operador ternario en este y otros lenguajes es que cualquier código que se encuentre <expression>debe producir un resultado booleano cuando se evalúe: verdadero o falso.

En el caso de su ejemplo, reemplace "asignado a" en mi explicación con "agregado a", o similar para cualquier aritmética taquigráfica que esté utilizando, si corresponde.

Wayne Koorts
fuente
Tenga en cuenta si el comentario perfecto es apropiado :) Se omite cualquier explicación de por qué las expresiones de la izquierda "se agrupan" primero (es decir, porque +tiene mayor precedencia que el operador condicional / ternario (de hecho, el operador condicional es casi siempre el último) evaluado en cualquier expresión).
Gone Coding
10

El +=hace lo que quiere, pero en la declaración ternaria a la derecha de él, verifica si h.classNamees falso, que sería si no estuviera definido. Si es veraz (es decir, si el nombre de una clase ya está especificado), entonces el error se agrega con un espacio (es decir, agrega una nueva clase), de lo contrario, se agrega sin el espacio.

El código podría reescribirse como sugiere, pero debe especificar que h.classNamese usará para la comparación de veracidad, en lugar de usar su valor real, en el operador ternario, así que asegúrese de no molestarse con la concatenación de valores. al mismo tiempo que realiza su operación ternaria:

h.className = h.className + (h.className ? ' error' : 'error');
David Hedlund
fuente
13
bueno, sí, undefinedno es falso , solo se trata como si lo fuera
David Hedlund
4

El lado derecho del =operador se evalúa de izquierda a derecha. Entonces,

g.className = h.className + h.className ? ' error' : 'error';`

es equivalente a

h.className = (h.className + h.className) ? ' error' : 'error';

Ser equivalente a

h.className += h.className ? ' error' : 'error';

tienes que separar la declaración ternaria entre paréntesis

h.className = h.className + (h.className ? ' error' : 'error');
Justin Johnson
fuente
3
if (h.className) {
    h.className = h.className + ' error';
} else {
    h.className = h.className + 'error';
}

debe ser equivalente a:

h.className += h.className ? ' error' : 'error';
Darin Dimitrov
fuente
1

Sé que esta es una pregunta muy antigua, pero no estoy 100% satisfecho con ninguna de las respuestas, ya que todas parecen incompletas. Así que aquí vamos de nuevo desde los primeros principios:

El objetivo general del usuario:

Resumiendo el código: "Deseo agregar un errornombre de clase a una cadena, opcionalmente con un espacio inicial si ya hay nombres de clase en la cadena".

La solución más sencilla

Como señaló Kobi, hace 5 años, tener un espacio inicial en los nombres de las clases no causará ningún problema con ningún navegador conocido, por lo que la solución correcta más corta sería:

h.className += ' error';

Eso debería haber sido la respuesta real al problema real .


Sea como fuere, las preguntas que se hicieron fueron ...

1) ¿Por qué funcionó esto?

h.className += h.className ? ' error' : 'error'

El operador condicional / ternario funciona como una instrucción if, que asigna el resultado de sus rutas trueo falsea una variable.

Entonces ese código funcionó porque se evalúa simplemente como:

if (h.className IS NOT null AND IS NOT undefined AND IS NOT '') 
    h.className += ' error'
else
    h.className += 'error'

2) y ¿por qué se rompió esto?

h.className = h.className + h.className ? ' error' : 'error'

La pregunta dice "eso da un [n] error en mi consola", lo que puede inducirlo a pensar que el código no funciona . De hecho, el siguiente código se ejecuta, sin error , pero simplemente devuelve 'error' si la cadena no estaba vacía y 'error' si la cadena estaba vacía y por lo tanto no cumplía con los requisitos .

Ese código siempre da como resultado una cadena que contiene solo ' error'o 'error'porque evalúa este pseudo código:

if ((h.className + h.className) IS NOT null AND IS NOT undefined AND IS NOT '')
    h.className = ' error'
else
    h.className = 'error'

La razón de esto es que el operador de adición ( +a la gente común) tiene mayor "precedencia" (6) que el operador condicional / ternario (15). Sé que los números aparecen al revés

Precedencia simplemente significa que cada tipo de operador en un idioma se evalúa en un orden predefinido particular (y no solo de izquierda a derecha).

Referencia: precedencia del operador de JavaScript

Cómo cambiar el orden de evaluación:

Ahora que sabemos por qué falla, necesita saber cómo hacer que funcione.

Algunas otras respuestas hablan de cambiar la precedencia , pero no puedes . La precedencia está integrada en el idioma. Eso es solo un conjunto fijo de reglas ... Sin embargo, puede cambiar el orden de evaluación ...

La herramienta de nuestra caja de herramientas que puede cambiar el orden de evaluación es el operador de agrupación (también conocido como corchetes). Para ello, se asegura de que las expresiones entre corchetes se evalúen antes de las operaciones fuera de los corchetes. Eso es todo lo que hacen, pero es suficiente.

Los corchetes funcionan simplemente porque ellos (operadores de agrupación) tienen mayor precedencia que todos los demás operadores ("ahora hay un nivel 0").

Simplemente agregando corchetes, cambia el orden de evaluación para garantizar que la prueba condicional se realice primero, antes de la concatenación de cadenas simple:

h.className = h.className + (h.className ? ' error' : 'error')

Ahora dejaré esta respuesta para que no se vea entre las demás :)

Codificación ido
fuente
1

Me gustaría elegir una explicación de Wayne:

<variable> = <expression> ? <true clause> : <false clause>

Consideremos ambos casos:

case 1:
h.className += h.className ? 'true' : 'false'     
  • El operador de asignación funciona bien y el valor se agrega
  • cuando se ejecuta por primera vez, o / p: false
  • 2ª vez. o / p: falsetrue - los valores se siguen agregando

case2: h.className = h.className + h.className? 'verdadero Falso'

  • el resultado no es el mismo que en el caso 1
  • cuando se ejecuta por primera vez, o / p: false
  • 2ª vez. o / p: falso - los valores no se siguen agregando

explanation

En el código anterior, el caso 1 funciona bien

mientras que case2:

h.className = h.className + h.className ? 'true' : 'false'
is executed as 
 h.className = (h.className + h.className) ? 'true' : 'false'

h.className + h.className=> considerado como expresión para el operador ternario, ya que el operador ternario tiene mayor prioridad. entonces, siempre el resultado de la expresión ternaria simplemente se asigna

Necesita definir la precedencia usando corchetes

Debe definir el orden de evaluación que se considerará con la ayuda de paréntesis para que el caso 2 funcione como el caso 1

h.className = h.className + (h.className ? ' error' : 'error') 
Angelin Nadar
fuente
1
La terminología aquí no es del todo correcta. La precedencia es inherente al lenguaje, no la defines . En cambio, está definiendo el orden de evaluación mediante la introducción de corchetes (que tienen mayor precedencia que todos los demás operadores).
Gone Coding
@TrueBlueAussie Lo acepto. Agradezco tu intención leyendo +1
Angelin Nadar