Co konstruuje x = x || masz na myśli?


250

Debuguję JavaScript i nie mogę wyjaśnić, co to ||robi?

function (title, msg) {
  var title = title || 'Error';
  var msg   = msg || 'Error on Request';
}

Czy ktoś może mi podpowiedzieć, dlaczego ten facet używa var title = title || 'ERROR'? Czasami też widzę to bez vardeklaracji.


44
Ludzie już na to odpowiedzieli ... ale należy być bardzo świadomym faktu, że druga wartość jest wybierana, jeśli pierwszą wartością jest falsyNIE, TYLKO undefined. Liczba razy, które widziałam doWeDoIt = doWeDoIt || true, wystarcza, by płakać. (tj. doWeDoItteraz już nigdy nie będzie false)
Matt


4
Dla osób z doświadczeniem w C # operator podwójnej rury jest równoważny operatorowi zerowej koalescencji ??. JavaScript ocenia niepuste obiekty, takie jak true (lub lepiej evalualtes zerowe obiekty na false)
usr-local-ΕΨΗΕΛΩΝ

3
Nie czuj się źle - JS jest jedynym głupim językiem, który pozwala na to okropne kodowanie ... i uczy, że właściwe jest zagnieżdżanie każdej funkcji w liniach kodu i wyrzucanie ich, czyniąc je jednorazowymi i bezużytecznymi po raz drugi. :) Mam 30 lat na kodowanie i powstrzymałem się od dotykania JS do niedawna i czuję twój ból, wszystko, co mogę powiedzieć, to zachować „nie ma sensu, to tylko w JS” arkusz pod ręką jest to jedyny sposób, aby „ udało mi się! :)
Collin Chaffin

1
Proszę rozważyć zmianę zaakceptowanej odpowiedzi na moją odpowiedź .
Michał Perłakowski

Odpowiedzi:


210

Oznacza to, że titleargument jest opcjonalny. Więc jeśli wywołasz metodę bez argumentów, użyje ona domyślnej wartości "Error".

To jest skrót do pisania:

if (!title) {
  title = "Error";
}

Ten rodzaj skróconej sztuczki z wyrażeniami logicznymi jest również powszechny w Perlu. Z wyrażeniem:

a OR b

ocenia true, aczy bjest albo jest true. Więc jeśli ato prawda, nie musisz wcale sprawdzać b. Jest to tak zwana ocena logiczna zwarcia, więc:

var title = title || "Error";

w zasadzie sprawdza, czy titleocenia false. Jeśli tak, „wraca” "Error", w przeciwnym razie zwraca title.


3
Przepraszam, że jestem wybredny, ale argument nie jest opcjonalny, argument jest sprawdzany
themightybun

4
To NIE jest odpowiedź i zgadzam się z ostatnim komentarzem, że nawet nie jest opcjonalny. Żadna część tej odpowiedzi nie jest poprawna, nawet odwołanie do Perla, ponieważ instrukcja Perla faktycznie robi SENSE i jest oceniana w zupełnie inny sposób. JS ewaluuje znacznie bardziej „skonwertowaną” logiczną metodę logiczną, którą również uważam za bardziej mylącą w czytaniu / pisaniu. Odpowiedź poniżej zatytułowana „Co to jest operator podwójnej rury” jest w rzeczywistości poprawną odpowiedzią.
Collin Chaffin

198

Co to jest operator podwójnej rury ( ||)?

Operator podwójnej rury ( ||) jest operatorem logicznymOR . W większości języków działa to w następujący sposób:

  • Jeśli pierwsza wartość to false, sprawdza drugą wartość. Jeśli tak true, to wraca, truea jeśli tak false, to wracafalse .
  • Jeśli pierwszą wartością jest true, zawsze zwraca true, bez względu na drugą wartość.

Zasadniczo działa to tak:

function or(x, y) {
  if (x) {
    return true;
  } else if (y) {
    return true;
  } else {
    return false;
  }
}

Jeśli nadal nie rozumiesz, spójrz na tę tabelę:

      | true   false  
------+---------------
true  | true   true   
false | true   false  

Innymi słowy, jest to fałsz tylko wtedy, gdy obie wartości są fałszywe.

Czym różni się JavaScript?

JavaScript jest nieco inny, ponieważ jest to luźno napisany język . W takim przypadku oznacza to, że można użyć ||operatora z wartościami, które nie są wartościami logicznymi. Chociaż nie ma to sensu, możesz użyć tego operatora na przykład z funkcją i obiektem:

(function(){}) || {}

Co tu się dzieje?

Jeśli wartości nie są wartościami logicznymi, JavaScript dokonuje domyślnej konwersji na wartość logiczną . Oznacza to, że jeśli wartość jest falsey (np 0, "", null, undefined(patrz również wszystkie wartości falsey w JavaScript )), będzie ona traktowana jako false; w przeciwnym razie jest traktowane jakotrue .

Powyższy przykład powinien dać true, ponieważ pusta funkcja jest prawdziwa. Tak nie jest. Zwraca pustą funkcję. Jest tak, ponieważ ||operator JavaScript nie działa tak, jak napisałem na początku. Działa w następujący sposób:

  • Jeśli pierwszą wartością jest falsey , zwraca drugą wartość .
  • Jeśli pierwsza wartość jest zgodna z prawdą , zwraca pierwszą wartość .

Zaskoczony? W rzeczywistości jest „kompatybilny” z tradycyjnym ||operatorem. Można go zapisać jako następującą funkcję:

function or(x, y) {
  if (x) {
    return x;
  } else {
    return y;
  }
}

Jeśli przekażesz prawdziwą wartość jako x, zwraca ona x, to znaczy prawdziwą wartość. Więc jeśli użyjesz go później w ifklauzuli:

(function(x, y) {
  var eitherXorY = x || y;
  if (eitherXorY) {
    console.log("Either x or y is truthy.");
  } else {
    console.log("Neither x nor y is truthy");
  }
}(true/*, undefined*/));

dostajesz "Either x or y is truthy.".

Gdyby to xbył falsey, eitherXorYbyłoby y. W takim przypadku zrozumiałbyś, że "Either x or y is truthy."jeśli yto prawda; inaczej byś dostał "Neither x nor y is truthy".

Rzeczywiste pytanie

Teraz, gdy wiesz, jak ||działa operator, prawdopodobnie sam możesz zrozumieć, co to x = x || yznaczy. Jeśli xjest prawdą, xjest przypisane x, więc tak naprawdę nic się nie dzieje; w przeciwnym razie yjest przypisany do x. Jest powszechnie używany do definiowania domyślnych parametrów funkcji. Często jest to jednak uważane za złą praktykę programistyczną , ponieważ zapobiega przekazywaniu wartości falsey (niekoniecznie undefinedlub null) jako parametru. Rozważ następujący przykład:

function badFunction(/* boolean */flagA) {
  flagA = flagA || true;
  console.log("flagA is set to " + (flagA ? "true" : "false"));
}

Wygląda na ważne od pierwszego wejrzenia. Co by się jednak stało, gdybyś podał falsejako flagAparametr (ponieważ jest to wartość logiczna, tzn. Może być truelub false)? To się stanie true. W tym przykładzie nie ma możliwości ustawienia flagAna false.

Byłoby to lepszy pomysł, aby jednoznacznie sprawdzić, czy flagAto undefined, tak:

function goodFunction(/* boolean */flagA) {
  flagA = typeof flagA !== "undefined" ? flagA : true;
  console.log("flagA is set to " + (flagA ? "true" : "false"));
}

Chociaż jest dłuższy, zawsze działa i jest łatwiejszy do zrozumienia.


Możesz także użyć składni ES6 dla domyślnych parametrów funkcji , ale pamiętaj, że nie działa ona w starszych przeglądarkach (takich jak IE). Jeśli chcesz obsługiwać te przeglądarki, powiel swój kod za pomocą Babel .

Zobacz także Operatory logiczne w MDN .


13
+1 - zdecydowanie najbardziej poprawna i kompletna odpowiedź. A dla tych, którzy mają to pytanie (niektórzy z nas, weterani koderów, którzy są nowi w JS), z pewnością powinni skoncentrować się na całej tej odpowiedzi na tej linii: „Choć to nie ma sensu”, ponieważ ten „luźny typ” po prostu nigdy nie ma sensu tym z nas, którzy bez niego dorastali. Dla nas operator boolowski jest tylko tym i TYLKO tym ...... i ktokolwiek myślał, że dobrym pomysłem byłoby zatrzymanie się i przemyślenie jakiejś zwariowanej konwersji wartości innych niż boolowskie podczas odczytywania / pisania kodu , zapomniałem wziąć ich lekarstwa tego dnia! :)
Collin Chaffin

2
+1, w skrócie: title = title || 'Error'oznaczaif (title) { title = title; } else { title = 'Error'; }
Andre Elrico

Właściwie nie zgadzam się z linią „chociaż to nie ma sensu”. Rozumiem, że linia nie skompiluje się na przykład w C, ale jest dobrze zrozumiana, jeśli pochodzisz na przykład z Ruby, a nawet Groovy. W Ruby można to wyrazić jeszcze krócej, jak title ||= 'Error'.
Elliot Nelson

28

Jeśli tytuł nie jest ustawiony, użyj „BŁĄD” jako wartości domyślnej.

Bardziej ogólny:

var foobar = foo || default;

Odczytuje: Ustaw foobar na foolub default. Możesz nawet połączyć to wiele razy:

var foobar = foo || bar || something || 42;

1
Uznałem to za mylące, ponieważ zmienne mają tę samą nazwę. O wiele łatwiej, gdy nie.
Norbert Norbertson

14

Wyjaśniając to trochę więcej ...

||Operatora jest logical- oroperatora. Wynik jest prawdziwy, jeśli pierwsza część jest prawdziwa i jest prawdą, jeśli druga część jest prawdziwa i jest prawdą, jeśli obie części są prawdziwe. Dla jasności, oto tabela:

 X | Y | X || Y 
---+---+--------
 F | F |   F    
---+---+--------
 F | T |   T    
---+---+--------
 T | F |   T    
---+---+--------
 T | T |   T    
---+---+--------

Czy zauważysz coś tutaj? Jeśli Xjest prawdą, wynik jest zawsze prawdziwy. Więc jeśli wiemy, że Xto prawda, nie musimy wcale sprawdzać Y. Wiele języków implementuje zatem ewaluatory „zwarć” dla logicznych or(i logicznych - andpochodzących z innego kierunku). Sprawdzają pierwszy element, a jeśli to prawda, wcale nie zawracają sobie głowy sprawdzaniem drugiego. Wynik (logicznie) jest taki sam, ale pod względem wykonania istnieje potencjalnie ogromna różnica, jeśli drugi element jest drogi do obliczenia.

Co to ma wspólnego z twoim przykładem?

var title   = title || 'Error';

Spójrzmy na to. titleElementem jest przekazywana do funkcji. W JavaScript, jeśli nie podasz parametru, domyślnie przyjmuje wartość zerową. Również w JavaScript, jeśli twoja zmienna ma wartość zerową, operatory logiczne uznają ją za fałsz. Więc jeśli funkcja jest wywoływana z podanym tytułem, jest to wartość niefałszowa, a zatem przypisana do zmiennej lokalnej. Jeśli jednak nie zostanie podana wartość, jest to wartość zerowa, a zatem fałsz. orOperator logiczny ocenia następnie drugie wyrażenie i zwraca zamiast tego „Błąd”. Zatem teraz zmienna lokalna otrzymuje wartość „Błąd”.

Działa to z powodu implementacji wyrażeń logicznych w JavaScript. Nie zwraca właściwej wartości logicznej ( truelub false), ale zwraca wartość podaną zgodnie z niektórymi regułami co do tego, co jest uważane za równoważne, truea co za równoważne false. Sprawdź swoją referencję JavaScript, aby dowiedzieć się, co JavaScript uważa za prawdę lub fałsz w kontekście logicznym.


8

Podwójna rura oznacza logiczne „LUB”. Tak naprawdę nie dzieje się tak, gdy „parametr nie jest ustawiony”, ponieważ ściśle w javascript, jeśli masz taki kod:

function foo(par) {
}

Potem dzwoni

foo()
foo("")
foo(null)
foo(undefined)
foo(0)

nie są równoważne.

Podwójna rura (||) wyrzuci pierwszy argument na wartość logiczną, a jeśli wynikowa wartość logiczna jest prawdziwa - wykonaj przypisanie, w przeciwnym razie przypisze odpowiednią część.

Ma to znaczenie, jeśli zaznaczysz parametr nieustawiony.

Powiedzmy, że mamy funkcję setSalary, która ma jeden opcjonalny parametr. Jeśli użytkownik nie poda parametru, należy użyć wartości domyślnej 10.

jeśli wykonasz taką kontrolę:

function setSalary(dollars) {
    salary = dollars || 10
}

Daje to nieoczekiwany wynik podczas połączenia

setSalary(0) 

Nadal ustawi 10 zgodnie z opisanym powyżej przepływem.


8

Zasadniczo sprawdza, czy wartość przed || przyjmuje wartość true, jeśli tak, przyjmuje tę wartość, jeśli nie, przyjmuje wartość po ||.

Wartości, dla których przyjmie wartość po || (o ile pamiętam):

  • nieokreślony
  • fałszywe
  • 0
  • '' (Ciąg zerowy lub zerowy)

1
false || null || niezdefiniowany || 0 || „” || „zapomniałeś null”
Dziamid

7

Chociaż odpowiedź Cletusa jest poprawna, uważam, że należy dodać więcej szczegółów w odniesieniu do „ewaluacji do fałszu” w JavaScript.

var title = title || 'Error';
var msg   = msg || 'Error on Request';

Nie tylko sprawdza, czy podano tytuł / msg, ale także czy którykolwiek z nich jest fałszywy . tj. jeden z następujących:

  • fałszywe.
  • 0 (zero)
  • "" (pusta struna)
  • zero.
  • nieokreślony.
  • NaN (specjalna wartość liczbowa oznaczająca liczbę inną niż!)

Więc w kolejce

var title = title || 'Error';

Jeśli tytuł jest zgodny z prawdą (tj. Nie jest fałszem, więc tytuł = „titleMessage” itp.), To operator logiczny OR (||) znalazł jedną „prawdziwą” wartość, co oznacza, że ​​zwraca wartość true, więc powoduje zwarcie i zwraca prawdziwa wartość (tytuł).

Jeśli tytuł jest fałszem (tj. Jedną z powyższych list), to operator logiczny OR (||) znalazł wartość „fałsz” i teraz musi ocenić drugą część operatora, „Błąd”, która ma wartość true i dlatego jest zwracany.

Wydaje się również (po szybkim eksperymentowaniu z konsolą firebug), że obie strony operatora oceniają na fałsz, zwraca drugi operator „fałsz”.

to znaczy

return ("" || undefined)

zwraca niezdefiniowany, prawdopodobnie pozwala to na użycie zachowania opisanego w tym pytaniu podczas próby ustawienia domyślnego tytułu / wiadomości na „”. tj. po uruchomieniu

var foo = undefined
foo = foo || ""

foo byłoby ustawione na „”


5

operator podwójnej rury

czy ten przykład jest przydatny?

var section = document.getElementById('special');
if(!section){
     section = document.getElementById('main');
}

może też być

var section = document.getElementById('special') || document.getElementById('main');

4

Aby dodać wyjaśnienie do wszystkich powiedzianych przede mną, powinienem podać kilka przykładów na zrozumienie logicznych pojęć.

var name = false || "Mohsen"; # name equals to Mohsen
var family = true || "Alizadeh" # family equals to true

Oznacza to, że jeśli lewa strona zostanie uznana za prawdziwą instrukcję, zostanie zakończona, a lewa strona zostanie zwrócona i przypisana do zmiennej. w innych przypadkach prawa strona zostanie zwrócona i przypisana.

A operator ma odwrotną strukturę jak poniżej.

var name = false && "Mohsen" # name equals to false
var family = true && "Alizadeh" # family equals to Alizadeh

3

|| jest logicznym operatorem OR. Podobnie jak w javascript, niezdefiniowane, null, 0, false są uważane za wartości fałszowania .

To po prostu znaczy

true || true = true
false || true = true
true || false = true
false || false = false

undefined || "value" = "value"
"value" || undefined = "value"
null || "value" = "value"
"value" || null = "value"
0 || "value" = "value"
"value" || 0 = "value"
false || "value" = "value"
"value" || false = "value"

2

Cytat: „Co oznacza konstrukcja x = x || y?”

Przypisywanie wartości domyślnej.

Oznacza to podanie wartości domyślnej od y do x , w przypadku gdy x nadal czeka na swoją wartość, ale jeszcze jej nie otrzymał lub celowo pominięto w celu przywrócenia wartości domyślnej.


To jest dokładne znaczenie konstruktu i jedyne jego znaczenie. I było to w dużej mierze podprogram w pisaniu funkcji, które można było pobrać jako prototypy, samodzielne funkcje, a także jako pożyczone metody do zastosowania w innym elemencie. Tam, gdzie jego głównym i jedynym obowiązkiem była modyfikacja odniesienia do celu. Przykład: function getKeys(x) { x = x || this ; .... }który może być używany bez modyfikacji jako samodzielna funkcja, jako metoda właściwości w prototypach oraz jako metoda elementu, który może uzyskać inny element jako argument jako „[element] .getKeys (anotherElement);`
Bekim Bacaj

-5

I muszę dodać jeszcze jedną rzecz: ta krótka skrótowa jest obrzydliwością. Niewłaściwie wykorzystuje przypadkową optymalizację interpretera (nie zawracając sobie głowy drugą operacją, jeśli pierwsza jest prawdziwa) do kontrolowania zadania. Takie zastosowanie nie ma nic wspólnego z celem operatora. Nie wierzę, że należy go kiedykolwiek wykorzystać.

Wolę operator trójskładnikowy do inicjalizacji, np.

var title = title?title:'Error';

Wykorzystuje jednowierszową operację warunkową do poprawnego celu. Nadal gra w nieestetyczne gry z prawdomównością, ale dla Ciebie to Javascript.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.