JavaScript OR (||) объяснение назначения переменной

351

Учитывая этот фрагмент JavaScript ...

var a;
var b = null;
var c = undefined;
var d = 4;
var e = 'five';

var f = a || b || c || d || e;

alert(f); // 4

Может кто-нибудь объяснить мне, как называется эта техника (мое лучшее предположение - в названии этого вопроса!)? И как / почему это работает точно?

Насколько я понимаю, переменной fбудет присвоено ближайшее значение (слева направо) первой переменной, которая имеет значение, которое не является ни нулевым, ни неопределенным, но мне не удалось найти много справочных материалов об этой технике и видел, что это используется много.

Кроме того, эта техника специфична для JavaScript? Я знаю, что выполнение чего-то подобного в PHP приведет fк истинному логическому значению, а не к значению самого dсебя.

chattsm
источник
4
Старый вопрос, но о PHP, есть конструкция , вы можете использовать: $f=$a or $f=$b or $f=$c; // etc. В PHP есть ||оператор и orоператор, которые выполняют одну и ту же работу; однако orоценивается после присвоения, в то время ||как оценивается до. Это также даст вам стиль $a=getSomething() or die('oops');
perlish
1
В PHP 5.3 вы можете опустить среднюю часть троичного оператора, основываясь на этом ... Вы также можете вырезать это немного короче примерно так: $f = $a ?: $b ?: $c;
Рей
1
Начиная с PHP 7 вы можете использовать ??для этого. $a = $b ?? 'default'
Спенсер Раскин
@SpencerRuskin, так $aбудет ли присвоено значение $bif $btrue, other 'default'?
oldboy
Вот так. Посмотрите на раздел об операторе объединения нулей
Спенсер Раскин,

Ответы:

212

См. Оценку короткого замыкания для объяснения. Это распространенный способ реализации этих операторов; это не уникально для JavaScript.

размотать
источник
57
Просто помните о «гоче», заключающейся в том, что последний всегда будет назначен, даже если все они неопределенные, нулевые или ложные. Установка того, что вы знаете, не является ложным, нулевым или неопределенным в конце цепочки - хороший способ показать, что ничего не найдено.
Эрик Реппен
Я видел эту технику в течение многих лет, но что меня поразило именно тогда, когда я захотел ее использовать, так это то, что результат выражения не приведен к логическому значению. Вы не можете позже сделать if( true == f ). Если целое число было сохранено в f, то этот тест всегда будет возвращать false.
9
На самом деле, вы можете сделать if(true == f), что так же, как if(f): тест пройдет. Если вы хотите , чтобы также проверить тип из f, использовать строгое сравнение: if(true === f), который подведет действительно.
Альсиенде
5
Да, оценка короткого замыкания является распространенным явлением. Но различие здесь заключается в том, как JavaScript возвращает последнее значение, которое остановило выполнение. Ответ @ Anurag намного лучше объясняет это.
Ben.12
не уверен, что это лучшее объяснение для начинающих. Я бы порекомендовал: javascript.info/logical-operators
csguy
198

Это сделано для присвоения значения по умолчанию , в данном случае значения y, если xпеременная ложная .

Логические операторы в JavaScript могут возвращать операнд, а не всегда логический результат, как в других языках.

Оператор логического ИЛИ ( ||) возвращает значение своего второго операнда, если первый является ложным, в противном случае возвращается значение первого операнда.

Например:

"foo" || "bar"; // returns "foo"
false || "bar"; // returns "bar"

Falsy значения являются те , которые принуждают к falseпри использовании в логическом контексте, и они 0, null, undefined, пустая строка, NaNи, конечно же false.

CMS
источник
1
+1 Есть еще такой оператор? Или ||эксклюзив.
ОскарРиз
9
@Support (@Oscar): Логический &&оператор имеет аналогичное поведение, он возвращает значение первого операнда, если он сам по себе ложный, и возвращает значение второго операнда, только если первый операнд верен , например, ("foo" && "bar") == "bar"и(0 && "bar") == 0
CMS
12
Ложь на самом деле является техническим термином.
ChaosPandion
8
Итак, мы узнали о ||, &&, "Falsy" и "Truly" в этом посте. Лучший ответ с «скрытыми» подарками.
Алекс
4
@ Алекс NB: «Правда» (! «Действительно»)
ухабистый
183

Javacript использует оценку короткого замыкания для логических операторов ||и &&. Однако он отличается от других языков тем, что возвращает результат последнего значения, остановившего выполнение, вместо значения trueили false.

Следующие значения считаются ложными в JavaScript.

  • ложный
  • значение NULL
  • "" (пустой строкой)
  • 0
  • бабушка
  • не определено

Игнорируя правила приоритета операторов и сохраняя простоту, следующие примеры показывают, какое значение остановило оценку и в результате возвращается.

false || null || "" || 0 || NaN || "Hello" || undefined // "Hello"

Первые 5 значений NaNявляются ложными, поэтому все они оцениваются слева направо до тех пор, пока оно не встретит первое истинное значение - "Hello"что делает все выражение истинным, поэтому все дальнейшее повышение не будет оцениваться и "Hello"будет возвращено в результате выражения , Аналогично в этом случае:

1 && [] && {} && true && "World" && null && 2010 // null

Первые 5 значений являются достоверными и оцениваются до тех пор, пока не встретятся первое ложное значение ( null), которое делает выражение ложным, поэтому 2010больше не оценивается и nullвозвращается в результате выражения.

В приведенном вами примере используется это свойство JavaScript для выполнения назначения. Он может использоваться где угодно, где вам нужно получить первое истинное или ложное значение среди набора значений. Этот код ниже будет присвоить значение , "Hello"чтобы , bкак это делает его легче присвоить значение по умолчанию, вместо того , чтобы делать if-другую проверку.

var a = false;
var b = a || "Hello";

Вы можете назвать приведенный ниже пример использованием этой функции, и я считаю, что это затрудняет чтение кода.

var messages = 0;
var newMessagesText = "You have " + messages + " messages.";
var noNewMessagesText = "Sorry, you have no new messages.";
alert((messages && newMessagesText) || noNewMessagesText);

Внутри оповещения мы проверяем, messagesявляется ли ложным, и если да, то оцениваем и возвращаем noNewMessagesText, иначе оцениваем и возвращаем newMessagesText. Поскольку в этом примере это ложно, мы останавливаемся на noNewMessagesText и предупреждаем "Sorry, you have no new messages.".

Анураг
источник
36
Это лучший ответ, на мой взгляд, из-за следующего объяснения:However, it's different to other languages in that it returns the result of the last value that halted the execution, instead of a true, or false value.
мастази
1
@mastazi Да, это должно быть выделено жирным шрифтом ИМХО.
Noober
6
Должен быть ответ, он показывает значения, выбранные для тестовых случаев.
Дадан
Согласен, это мой любимый ответ, так как он конкретно касается вопросов назначения переменных JavaScript. Кроме того, если вы решите использовать троичную в качестве одной из последующих переменных для проверки присвоения (после оператора), вы должны заключить троичную скобку в скобки, чтобы оценка присвоения работала правильно.
Joey T
это должен быть принятый ответ
Алан
49

Переменные Javascript не являются типизированными, поэтому f можно присвоить целочисленное значение, даже если оно было назначено с помощью логических операторов.

F присваивается ближайшее значение, которое не эквивалентно ложному . Итак, 0, false, null, undefined, все передаются:

alert(null || undefined || false || '' || 0 || 4 || 'bar'); // alerts '4'
Alsciende
источник
13
Не забудьте ''также равный false в этом случае.
Разбойник
Upvote за то, что указал на то, f is assigned the NEAREST valueчто здесь очень важно.
Steviejay
3
«Ближайший» не совсем верно, хотя у него есть такой вид. Логический ||оператор, будучи логическим оператором, имеет два операнда: левую и правую стороны. Если левая сторона ||является truthy , операция рассасывается к левой стороне и правой стороне игнорируются. Если левая сторона ложная , она переходит в правую сторону. Так что на null || undefined || 4 || 0самом деле решает, undefined || 4 || 0что решает, 4 || 0что решает, чтобы 4.
devios1
29

В этом нет никакой магии. Булевы выражения вроде a || b || c || dлениво оцениваются. Interpeter ищет значение a, оно не определено, поэтому оно ложно, поэтому оно движется дальше, затем оно видит, bкакое значение равно нулю, что все еще дает ложный результат, поэтому оно движется дальше, а затем c- ту же историю. Наконец, он видит dи говорит «да, это не нуль, поэтому у меня есть результат», и он присваивает его конечной переменной.

Этот прием будет работать во всех динамических языках, которые лениво оценивают логические выражения при коротком замыкании. В статических языках он не будет компилироваться (ошибка типа). В языках, которые стремятся оценить логические выражения, он вернет логическое значение (т. Е. В данном случае true).

Marcin
источник
6
В довольно статическом языке C # можно использовать ?? оператор а-ля: объект f = a ?? б ?? с ?? д ?? е;
Герцмейстер
2
Герцмейстер - спасибо! Я этого не знал ?? оператор может быть связан в C # и использоваться в ленивых методах оценки
Марек
3
Как уже упоминалось, последний dбудет назначен независимо от того, был он нулевым или неопределенным или нет.
BlackVegetable
Одно небольшое исправление: ||оператор всегда разрешает весь операнд правой стороны, когда левая сторона ложная. Будучи логическим оператором, он видит только два входа: левую и правую стороны. Парсер не видит их как последовательность терминов, поэтому он фактически не останавливается, когда находит первое истинное значение, если только это значение не является левым операндом другого ||.
devios1
8

Этот вопрос уже получил несколько хороших ответов.

Таким образом, этот метод использует возможность компилирования языка. То есть JavaScript «закорачивает» оценку булевых операторов и возвращает значение, связанное либо с первым непустым значением переменной, либо с тем, что содержит последняя переменная. См. Объяснение Анурага тех ценностей, которые будут оцениваться как ложные.

Использование этой техники не является хорошей практикой по нескольким причинам; Однако.

  1. Читаемость кода: это использует логические операторы, и если поведение того, как это компилируется, не понято, то ожидаемый результат будет логическим значением.
  2. Стабильность: при этом используется функция компиляции языка, которая несовместима между несколькими языками, и из-за этого она потенциально может быть изменена в будущем.
  3. Документированные функции: существует альтернатива, которая удовлетворяет эту потребность и совместима с другими языками. Это будет троичный оператор:

    ()? значение 1: значение 2.

Использование тернарного оператора требует немного большего набора текста, но оно четко различает оцениваемое логическое выражение и назначаемое значение. Кроме того, он может быть объединен в цепочку, поэтому типы назначений по умолчанию, выполняемые выше, могут быть воссозданы.

var a;
var b = null;
var c = undefined;
var d = 4;
var e = 'five';

var f =  ( a ) ? a : 
                ( b ) ? b :
                       ( c ) ? c :
                              ( d ) ? d :
                                      e;

alert(f); // 4
WSimpson
источник
potentially be targeted for change in the future.да, но это не относится к javascript.
Дадан
Пришел сюда и увидел все вышеперечисленные ответы и подумал про себя, что что-то просто отвлекает от задания. Я только что читал «Чистый код» Роберта С. Мартина, и этот тип назначения определенно нарушает правило «не иметь побочных эффектов» ... в то время как сам автор утверждает, что его книга является лишь одним из многих методов создания хорошего кода, я был все еще удивлен, что никто больше не возражал против такого рода назначения. +1
Альберт Ротман
Спасибо за ответ. Я думаю, что больше людей должны учитывать побочные эффекты при написании кода, но пока кто-то не потратил много времени на поддержку кода других людей. Они часто не считают это.
WSimpson
1
Вы действительно думаете, что чудовище яснее, чем a || b || c || d || e?
devios1
1
@ AlbertRothman Я не вижу никаких побочных эффектов. Ничего не мутирует. Это просто сокращение для слияния нулей, что довольно часто встречается во многих языках.
devios1
7

Вернуть вывод первого истинного значения .

Если все ложно, вернуть последнее ложное значение.

Пример:-

  null || undefined || false || 0 || 'apple'  // Return apple
Аршид К.В.
источник
4

Он устанавливает новую переменную ( z) либо в значение x«истинно» (не ноль, допустимый объект / массив / функция / что бы то ни было) или yиное. Это относительно распространенный способ предоставления значения по умолчанию на случай, если xего не существует.

Например, если у вас есть функция, которая принимает необязательный параметр обратного вызова, вы можете предоставить обратный вызов по умолчанию, который ничего не делает:

function doSomething(data, callback) {
    callback = callback || function() {};
    // do stuff with data
    callback(); // callback will always exist
}
Мэтью Крамли
источник
1

Это означает, что если xустановлено, значение для zбудет x, в противном случае, если yустановлено, то его значение будет установлено какz значение.

это так же, как

if(x)
  z = x;
else
  z = y;

Это возможно, потому что логические операторы в JavaScript не возвращают логические значения, но значение последнего элемента, необходимого для завершения операции (в предложении ИЛИ это будет первое не ложное значение, в предложении И это будет последнее ). Если операция не выполняется, falseвозвращается.

Андрис
источник
5
это не верно! если (х) {г = х; } else {z = y;} если первое значение равно false, второе значение всегда присваивается вне зависимости от того, какое значение на самом деле является.
evilpie
За исключением того, что я думаю, что он просто назначает y для z, если x ложно . Это то, как это работает для меня в FF, конечно, это также зависит от реализации.
tvanfosson
7
Последняя часть о возвращении ложного не соответствует действительности (не каламбур). Если первое значение равно false, ||оператор просто возвращает второе значение, независимо от того, верно оно или нет.
Мэтью Крамли
-1. Ваш эквивалентный фрагмент кода является точным, но важным моментом является то, что zустанавливается значение, xесли это значение является достоверным . В противном случае он устанавливается на значение y. Это означает, что если xустановлено, например, 0или пустая строка "", это не будет делать то, что вы говорите, так как эти значения ложные .
Дэниел Кэссиди
1

Это называется оператор короткого замыкания.

Оценка короткого замыкания говорит, что второй аргумент выполняется или оценивается, только если первого аргумента недостаточно для определения значения выражения. когда первый аргумент функции OR (||) оценивается как true, общее значение должно быть true.

Его также можно использовать для установки значения по умолчанию для аргумента функции.

function theSameOldFoo(name){ 
  name = name || 'Bar' ;
  console.log("My best friend's name is " + name);
}
theSameOldFoo();  // My best friend's name is Bar
theSameOldFoo('Bhaskar');  // My best friend's name is Bhaskar`
Виджай
источник
0

Он будет оценивать X и, если X не равен NULL, пустой строке или 0 (логическое ложное значение), то он назначит его z. Если X - ноль, пустая строка или 0 (логическое ложь), тогда он назначит y для z.

var x = '';
var y = 'bob';
var z = x || y;
alert(z);

Будет выводить «Боб»;

tvanfosson
источник
Вы должны уточнить, что вы подразумеваете под «пустым». Пустые строки приводят к false, но пустые массивы или объекты приводят к true.
Дэниел Кэссиди
@Daniel "null, empty или 0" - null будет применяться по отношению к массивам и объектам. Точка занята, хотя.
tvanfosson
0

Согласно сообщению в блоге Билла Хиггинса ; идиома логического ИЛИ назначения Javascript (февраль 2007 г.), это поведение верно с версии 1.2 (по крайней мере)

Он также предлагает другое использование для этого (цитируется): « облегченная нормализация различий между браузерами »

// determine upon which element a Javascript event (e) occurred
var target = /*w3c*/ e.target || /*IE*/ e.srcElement;
Алон Бронтман
источник