Precedência de operador com operador Javascript ternário

116

Não consigo entender a primeira parte deste código (+ =) em combinação com o operador ternário.

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

Acho que esse código funciona da seguinte maneira:

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

Mas isso não está correto porque dá um erro no meu console.

Portanto, minha pergunta é como devo interpretar este código corretamente?

Baijs
fonte

Respostas:

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

Você quer que o operador trabalhe h.className, é melhor ser específico sobre isso.
Claro, nenhum dano deve vir h.className += ' error', mas isso é outra questão.

Além disso, observe que +tem precedência sobre o operador ternário: Precedência do operador JavaScript

Kobi
fonte
3
Eu acho que deve ser observado que, embora nenhum dano possa ocorrer h.className += ' error', ele também deixa um espaço em branco no início da string, se ela estava inicialmente vazia. Eu acredito que o objetivo da operação ternária é produzir uma string de aparência limpa.
JMTyler
@JMTyler - Isso é exatamente o que eu estava indicando - Se tudo for feito apenas para deixar um espaço desde o início, não vale a pena. (casos extremos incluem seletores exatos de jQuery ou XPath). De qualquer forma, obrigado.
Kobi
@Kobi +1 para o aviso de precedência do operador sozinho!
Ed Chapel de
129

Pense desta forma:

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

A forma como a instrução é executada é basicamente a seguinte:

  1. É <expression>avaliada como verdadeira ou avaliada como falsa?
  2. Se for <expression>avaliado como verdadeiro, o valor de <true clause>será atribuído a <variable>, <false clause>será ignorado e a próxima instrução será executada.
  3. Se for <expression>avaliado como falso, então <true clause>será ignorado e o valor de <false clause>será atribuído a <variable>.

O importante a ser percebido com o operador ternário nesta e em outras linguagens é que, qualquer que seja o código, <expression>deve produzir um resultado booleano quando avaliado: verdadeiro ou falso.

No caso do seu exemplo, substitua "atribuído a" em minha explicação por "adicionado a", ou similar para qualquer aritmética abreviada que você esteja usando, se houver.

Wayne Koorts
fonte
Observe se o comentário perfeito é apropriado :) Ele ignora qualquer explicação de porque as expressões do lado esquerdo "agrupadas" primeiro (ou seja, porque +tem maior precedência do que o operador condicional / ternário (na verdade o operador condicional é quase sempre o último avaliado em qualquer expressão).
Gone Coding
10

O +=faz o que você quer, mas no enunciado ternário à direita dele, verifica se h.classNameestá falsey, o que seria se não estivesse definido. Se for verdade (ou seja, se um nome de classe já estiver especificado), o erro será adicionado com um espaço (ou seja, adicionando uma nova classe), caso contrário, será adicionado sem o espaço.

O código pode ser reescrito como você sugere, mas você precisa especificar que h.classNamedeve ser usado para comparação de veracidade, em vez de usar seu valor real, no operador ternário, portanto, certifique-se de não se preocupar com a concatenação de valores ao mesmo tempo que faz sua operação ternária:

h.className = h.className + (h.className ? ' error' : 'error');
David Hedlund
fonte
13
bem, sim, undefinednão é falso , é apenas tratado como se fosse
David Hedlund
4

O lado direito do =operador é avaliado da esquerda para a direita. Assim,

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

é equivalente a

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

Para ser equivalente a

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

você tem que separar a declaração ternária entre parênteses

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

deve ser equivalente a:

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

Eu sei que esta é uma pergunta muito antiga, mas não estou 100% feliz com nenhuma das respostas, pois todas parecem incompletas. Então, aqui vamos nós de novo dos primeiros princípios:

O objetivo geral do usuário:

Resumindo o código: "Desejo adicionar umerror nome de classe a uma string, opcionalmente com um espaço inicial se já houver nomes de classe na string."

Solução mais simples

Como Kobi apontou, 5 anos atrás, ter um espaço à esquerda nos nomes das classes não causará problemas com nenhum navegador conhecido, então a solução correta mais curta seria:

h.className += ' error';

Essa deveria ter sido a resposta real para o problema real .


Seja como for, as perguntas feitas foram ...

1) Por que isso funcionou?

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

O operador condicional / ternário funciona como uma instrução if, que atribui o resultado de seus caminhos trueou falsea uma variável.

Portanto, esse código funcionou porque é avaliado simplesmente como:

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

2) e por que isso quebrou?

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

A questão afirma "isso dá um erro [n] em meu console", o que pode induzi-lo a pensar que o código não funciona . Na verdade, o código a seguir é executado, sem erros , mas simplesmente retorna 'erro' se a string não estava vazia e 'erro' se a string estava vazia e, portanto , não atendeu aos requisitos .

Esse código sempre resulta em uma string que contém apenas ' error'ou 'error'porque é avaliada para este pseudocódigo:

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

A razão para isso é que o operador de adição ( +para o povo comum) tem maior "precedência" (6) do que o operador condicional / ternário (15). Eu sei que os números aparecem ao contrário

Precedência significa simplesmente que cada tipo de operador em uma linguagem é avaliado em uma ordem predefinida específica (e não apenas da esquerda para a direita).

Referência: Precedência do operador Javascript

Como alterar a ordem de avaliação:

Agora que sabemos por que falha, você precisa saber como fazê-lo funcionar.

Algumas outras respostas falam sobre como mudar a precedência , mas você não pode . A precedência está embutida na linguagem. Isso é apenas um conjunto fixo de regras ... No entanto, você pode alterar a ordem de avaliação ...

A ferramenta em nossa caixa de ferramentas que pode alterar a ordem de avaliação é o operador de agrupamento (também conhecido como colchetes). Ele faz isso garantindo que as expressões entre colchetes sejam avaliadas antes das operações fora dos colchetes. É tudo o que eles fazem, mas é o suficiente.

Os colchetes funcionam simplesmente porque eles (operadores de agrupamento) têm precedência mais alta do que todos os outros operadores ("agora existe um nível 0").

Simplesmente adicionando colchetes, você altera a ordem de avaliação para garantir que o teste condicional seja executado primeiro, antes da concatenação de string simples:

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

Vou deixar esta resposta para enferrujar invisível entre as outras :)

Gone Coding
fonte
1

Eu gostaria de escolher a explicação de wayne:

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

Vamos considerar os dois casos:

case 1:
h.className += h.className ? 'true' : 'false'     
  • operador de atribuição funciona bem e o valor é anexado
  • quando é executado pela primeira vez, o / p: falso
  • 2ª vez. o / p: falso verdadeiro - os valores continuam acrescentando

case2: h.className = h.className + h.className? 'verdadeiro falso'

  • o resultado não é o mesmo do caso 1
  • quando é executado pela primeira vez, o / p: falso
  • 2ª vez. o / p: falso - os valores não continuam acrescentando

explanation

No código acima, o caso 1 funciona bem

enquanto caso 2:

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 expressão para o operador ternário, já que o operador ternário tem maior precedência. então, sempre o resultado da expressão ternária é apenas atribuído

Você precisa definir a precedência usando colchetes

Você precisa definir a ordem de avaliação a ser considerada com a ajuda de colchetes para o caso 2 funcionar como o caso 1

h.className = h.className + (h.className ? ' error' : 'error') 
Angelin Nadar
fonte
1
A terminologia aqui não é muito correta. A precedência é inerente à linguagem, você não a define . Em vez disso, você está definindo a ordem de avaliação introduzindo colchetes (que têm precedência mais alta do que todos os outros operadores).
Gone Coding
@TrueBlueAussie Eu aceito. Agradeço sua intenção ao ler +1
Angelin Nadar