Pierwszeństwo operatorów z operatorem trójargumentowym Javascript

116

Nie mogę objąć głowy pierwszą częścią tego kodu (+ =) w połączeniu z operatorem trójskładnikowym.

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

Sposób, w jaki myślę, że ten kod działa, jest następujący:

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

Ale to nie jest poprawne, ponieważ powoduje to błąd w mojej konsoli.

Więc moje pytanie brzmi: jak mam poprawnie interpetować ten kod?

Baijs
źródło

Odpowiedzi:

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

Chcesz, aby operator pracował h.className, lepiej bądź dokładny.
Oczywiście nie powinno z tego wynikać nic złego h.className += ' error', ale to już inna sprawa.

Należy również zauważyć, że +ma on pierwszeństwo przed operatorem trójargumentowym: pierwszeństwo operatora JavaScript

Kobi
źródło
3
Myślę, że należy zauważyć, że chociaż żadna szkoda nie powinna być spowodowana h.className += ' error', pozostawia również puste miejsce na początku ciągu, jeśli był początkowo pusty. Uważam, że celem tej trójskładnikowej operacji jest stworzenie czysto wyglądającego ciągu.
JMTyler,
@JMTyler - Dokładnie to wskazywałem - jeśli wszystko jest zrobione tylko po to, aby zachować miejsce od początku, nie jestem tego wart. (przypadek skrajny obejmuje dokładne selektory jQuery lub XPath). W każdym razie dzięki.
Kobi
@Kobi +1 dla samego ostrzeżenia o pierwszeństwie operatora!
Ed Chapel
129

Pomyśl o tym w ten sposób:

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

Sposób wykonania instrukcji jest w zasadzie następujący:

  1. Czy <expression>daje wynik prawda, czy też fałsz?
  2. Jeśli <expression>zwraca wartość true, to wartość <true clause>jest przypisywana do <variable>, <false clause>jest ignorowana i wykonywana jest następna instrukcja.
  3. Jeśli ma <expression>wartość false, to <true clause>jest ignorowane i <false clause>przypisywana jest wartość <variable>.

Ważną rzeczą, którą należy sobie uświadomić w przypadku operatora trójskładnikowego w tym i innych językach, jest to, że jakikolwiek kod się znajduje, <expression>po ocenie powinien dać wynik boolowski: prawda lub fałsz.

W twoim przykładzie zamień „przypisane do” w moim wyjaśnieniu na „dodane do” lub podobnie dla dowolnej skróconej arytmetyki, której używasz, jeśli w ogóle.

Wayne Koorts
źródło
Zwróć uwagę, czy idealny komentarz jest właściwy :) Pomija wszelkie wyjaśnienia dlaczego lewostronne wyrażenia są najpierw "zgrupowane" (tj. Ponieważ +mają większy priorytet niż operator warunkowy / trójskładnikowy (w rzeczywistości operator warunkowy jest prawie zawsze ostatnim oceniane w jakimkolwiek wyrażeniu)
Gone Coding
10

+=Robi to, co chcesz, ale w potrójnym oświadczenie na prawicy to, to sprawdza czy h.classNamejest falsey, które byłoby, gdyby to było niezdefiniowane. Jeśli jest to prawda (tj. Jeśli nazwa klasy jest już określona), to błąd jest dodawany ze spacją (tj. Dodawanie nowej klasy), w przeciwnym razie jest dodawany bez spacji.

Kod można przepisać tak, jak sugerujesz, ale musisz określić, że h.classNamema być używany do porównania prawdziwości, a nie do używania jego rzeczywistej wartości w operatorze trójskładnikowym, więc upewnij się, że nie zawracasz sobie głowy konkatenacją wartości w tym samym czasie, gdy wykonujesz swoją trójskładnikową operację:

h.className = h.className + (h.className ? ' error' : 'error');
David Hedlund
źródło
13
cóż, tak, undefinednie jest fałszywe , jest po prostu traktowane tak, jakby było
David Hedlund
4

Prawa strona =operatora jest oceniana od lewej do prawej. Więc,

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

jest równa

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

Aby być równoważnym

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

musisz oddzielić zdanie trójskładnikowe w nawiasach

h.className = h.className + (h.className ? ' error' : 'error');
Justin Johnson
źródło
3
if (h.className) {
    h.className = h.className + ' error';
} else {
    h.className = h.className + 'error';
}

powinien być odpowiednikiem:

h.className += h.className ? ' error' : 'error';
Darin Dimitrov
źródło
1

Wiem, że to bardzo stare pytanie, ale nie jestem w 100% zadowolony z żadnej z odpowiedzi, ponieważ wszystkie wydają się niekompletne. Więc znowu zaczynamy od pierwszych zleceniodawców:

Ogólny cel użytkownika:

Podsumowując kod: „Chciałbym dodać errornazwę klasy do łańcucha, opcjonalnie z początkiem spacji, jeśli w ciągu są już nazwy klas”.

Najprostsze rozwiązanie

Jak zauważył Kobi, 5 lat temu posiadanie wiodącej spacji w nazwach klas nie spowoduje żadnych problemów w żadnej ze znanych przeglądarek, więc najkrótszym poprawnym rozwiązaniem byłoby w rzeczywistości:

h.className += ' error';

To powinna być właściwa odpowiedź na rzeczywisty problem .


Tak czy inaczej, zadawano pytania ...

1) Dlaczego to zadziałało?

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

Operator warunkowy / trójskładnikowy działa jak instrukcja if, która przypisuje wynik swoich ścieżek truelub falsedo zmiennej.

Więc ten kod działał, ponieważ jest oceniany po prostu jako:

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

2) i dlaczego to się zepsuło?

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

Pytanie brzmi: „to daje [n] błąd w mojej konsoli”, co może wprowadzić Cię w błąd, myśląc, że kod nie działa . W rzeczywistości poniższy kod działa bez błędów , ale po prostu zwraca „błąd”, jeśli ciąg nie był pusty, i „błąd”, jeśli ciąg był pusty, a więc nie spełniał wymagań .

Ten kod zawsze daje w wyniku ciąg, który zawiera tylko ' error'lub 'error'dlatego, że jest wynikiem tego pseudokodu:

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

Powodem tego jest fakt, że operator dodawania ( +do zwykłego ludu) ma wyższy „priorytet” (6) niż operator warunkowy / trójskładnikowy (15). Wiem, że liczby pojawiają się wstecz

Pierwszeństwo oznacza po prostu, że każdy typ operatora w języku jest oceniany w określonej, predefiniowanej kolejności (a nie tylko od lewej do prawej).

Odniesienie: Pierwszeństwo operatorów JavaScript

Jak zmienić kolejność ocen:

Teraz wiemy, dlaczego się nie udaje, musisz wiedzieć, jak to działa.

Inne odpowiedzi mówią o zmianie priorytetu , ale nie możesz . Pierwszeństwo jest na stałe wpisane w język. To tylko ustalony zestaw reguł ... Możesz jednak zmienić kolejność ocen ...

Narzędziem w naszym zestawie narzędzi, które może zmienić kolejność oceny, jest operator grupowania (inaczej nawiasy). Czyni to poprzez zapewnienie, że wyrażenia w nawiasach są oceniane przed operacjami poza nawiasami. To wszystko, co robią, ale to wystarczy.

Nawiasy działają po prostu dlatego, że (operatory grupujące) mają wyższy priorytet niż wszystkie inne operatory („teraz jest poziom 0”).

Po prostu dodając nawiasy, zmieniasz kolejność obliczeń, aby upewnić się, że test warunkowy zostanie wykonany jako pierwszy, przed prostą konkatenacją ciągów:

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

Zostawię teraz tę odpowiedź niewidoczną wśród innych rdzy :)

Gone Coding
źródło
1

Chciałbym wybrać wyjaśnienie Wayne'a:

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

Rozważmy oba przypadki:

case 1:
h.className += h.className ? 'true' : 'false'     
  • operator przypisania działa dobrze, a wartość jest dodawana
  • gdy jest uruchamiany po raz pierwszy, o / p: false
  • Drugi raz. o / p: falsetrue - wartości są dołączane

case2: h.className = h.className + h.className? 'prawda fałsz'

  • wynik nie jest taki sam jak w przypadku 1
  • gdy jest uruchamiany po raz pierwszy, o / p: false
  • Drugi raz. o / p: false - wartości nie są dołączane

explanation

W powyższym kodzie przypadek 1 działa dobrze

podczas gdy przypadek2:

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

h.className + h.className=> traktowane jako wyrażenie dla operatora trójskładnikowego, ponieważ operator trójskładnikowy ma wyższy priorytet. więc zawsze przypisywany jest wynik wyrażenia trójskładnikowego

Musisz zdefiniować pierwszeństwo za pomocą nawiasów

Aby przypadek 2 działał jak przypadek 1, musisz zdefiniować kolejność ocen, które mają być brane pod uwagę, za pomocą nawiasów

h.className = h.className + (h.className ? ' error' : 'error') 
Angelin Nadar
źródło
1
Terminologia tutaj nie jest do końca poprawna. Pierwszeństwo jest nieodłącznym elementem języka, nie definiujesz go . Zamiast tego definiujesz kolejność oceny , wprowadzając nawiasy (które mają wyższy priorytet niż wszystkie inne operatory).
Gone Coding
@TrueBlueAussie Akceptuję to. dziękuję za zamiar przeczytania +1
Angelin Nadar