Пояснення присвоєння змінної JavaScript АБО (||)


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себе.


4
Старий питання, але про PHP, є конструкція , ви можете використовувати: $f=$a or $f=$b or $f=$c; // etc. У PHP є і ||оператор, і orоператор, які виконують ту саму роботу; однак orоцінюється після призначення, тоді ||як оцінюється раніше. Це також надає вам приємний стиль$a=getSomething() or die('oops');
Манго

1
У PHP 5.3 ви можете залишити середню частину потрійного оператора так, виходячи з цього ... Ви також можете вирізати це трохи коротше на щось подібне: $f = $a ?: $b ?: $c;
Rei

1
Станом на PHP 7, ви можете використовувати ??для цього. $a = $b ?? 'default'
Спенсер Раскін

@SpencerRuskin так $aбуде присвоєно значення $bif, якщо $bце правда, інше 'default'?
oldboy

Це вірно. Подивіться на розділ оператора нульового злиття на цій сторінці: php.net/manual/en/migration70.new-features.php
Спенсер Раскін

Відповіді:


212

Дивіться оцінку короткого замикання для пояснення. Це загальний спосіб реалізації цих операторів; це не властиво JavaScript.


57
Зауважте, що "gotcha" полягає в тому, що останній завжди буде призначений, навіть якщо вони всі визначені, недійсні або помилкові. Встановлення того, що ви знаєте, не є помилковим, нульовим або невизначеним у кінці ланцюга - це хороший спосіб сигналізувати, що нічого не знайдено.
Ерік Реппен

Я бачив цю техніку роками, але те, що мене вразило саме тоді, коли я хотів її використати, - це те, що результат вираження не кидається на булевий. Пізніше ви не можете зробити if( true == f ). Якщо ціле число було збережене у f, то цей тест завжди поверне помилковим.

9
Власне, можна зробити if(true == f), що те саме, що if(f): тест пройде. Якщо ви хочете , щоб також перевірити тип з f, використовувати суворе порівняння: if(true === f), який підведе дійсно.
Alsciende

5
Так, звичайна оцінка короткого замикання. Але відмінність тут полягає в тому, як JavaScript повертає останнє значення, яке зупинило виконання. @ Відповідь Анурага робить набагато кращу роботу з пояснення цього.
Бен.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.


1
+1 Чи є інший подібний оператор? Або ||ексклюзивно.
OscarRyz

9
@Support (@Oscar): Логічний &&оператор має аналогічну поведінку, він повертає значення першого операнда, якщо він сам по собі хибний, і повертає значення другого операнда, лише якщо перший є правдоподібним , наприклад, ("foo" && "bar") == "bar"і(0 && "bar") == 0
CMS

12
Фалсі насправді є технічним терміном.
ChaosPandion

8
Тож ми дізналися про ||, &&, "Falsy" та "Воістину" у цій публікації. Найкраща відповідь зі «прихованими» подарунками.
Олексій

4
@ Алекс NB: "Truthy" (! "Воістину")
Bumpy

183

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

Наступні значення вважаються хибними в JavaScript.

  • помилковий
  • нуль
  • "" (порожній рядок)
  • 0
  • Нан
  • невизначений

Чи не звертаючи уваги на першочерговість оператора правила, і тримати речі простими, такі приклади показують , яке значення призупинили оцінку, і отримує повертаються в якості результату.

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

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

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

Перші п'ять значень є правдоподібними і оцінюються до тих пір, поки воно не зустріне перше значення 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.
mastazi

1
@mastazi Так, він повинен вийти жирним шрифтом IMHO.
noober

6
Якщо відповідь повинна бути, вона показує значення, обрані в тестових випадках.
Дадан

Погоджено, це моя улюблена відповідь, оскільки вона конкретно стосується проблем з присвоєнням змінної JavaScript. Крім того, якщо ви вирішили використовувати тернар як одну з наступних змінних для тестування на присвоєння (після оператора), ви повинні загортати посилання в дужки, щоб оцінювання призначення працювало належним чином.
Joey T

49

Змінні Javascript не набираються, тому f може бути призначене ціле значення, навіть якщо воно було призначене через булеві оператори.

f присвоюється найближче значення, не еквівалентне хибному . Отже 0, false, null, undefined, всі передаються:

alert(null || undefined || false || '' || 0 || 4 || 'bar'); // alerts '4'

13
Не забувайте ''і в цьому випадку рівну помилку.
Бриганд

Оновлення для вказівки на те, 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ліниво оцінюються. Інтерпетер шукає значення a, воно не визначене, тому воно хибне, тому воно рухається далі, потім він бачить, bщо є нульовим, що все-таки дає хибний результат, тому він рухається далі, потім бачить c- та сама історія. Нарешті він бачить dі каже "так, це не нульово, тому я маю результат", і він призначає його до остаточної змінної.

Цей трюк буде працювати у всіх динамічних мовах, які виконують ледачу оцінку короткого замикання булевих виразів. У статичних мовах він не компілюється (помилка введення). У мовах, які прагнуть оцінювати булеві вирази, воно поверне логічне значення (тобто істинне в даному випадку).


6
На досить статичній мові C # можна використовувати ?? оператор á la: об'єкт f = a ?? б ?? c ?? d ?? е;
herzmeister

2
герцмейстер - спасибі! Я цього не знав ?? Оператор може бути прикутий до C # і використаний у техніці лінивої оцінки
Марек

3
Як було зазначено в іншому місці, цей останній dбуде присвоєний тим, чи було це чи не визначено чи ні, чи ні.
BlackVegetable

Одне невелике виправлення: ||оператор завжди підпадає до всього правого бічного операнда, коли лівий бік хибний. Будучи булевим оператором, він бачить лише два входи: лівий і правий. Аналізатор не сприймає їх як ряд термінів, тому він фактично не зупиняється, коли знаходить перше значення трити, якщо це значення також не є лівим операндом іншого ||.
devios1

8

На це запитання вже отримано кілька хороших відповідей.

Підсумовуючи це, ця методика використовує особливість того, як складається мова. Тобто JavaScript "коротке замикання" оцінює булеві оператори і поверне значення, пов'язане або з першим неправдивим значенням змінної, або з тим, що містить остання змінна. Див. Пояснення Анурага тих значень, які будуть оцінені на хибних.

Використання цієї методики не є хорошою практикою з кількох причин; проте

  1. Читання коду: для цього використовуються оператори Boolean, і якщо поведінка способу компіляції не зрозуміла, очікуваний результат буде булевим значенням.
  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

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якщо вона "truthy" (не нульова, дійсний об'єкт / масив / функція / як би вона не була) або yіншим чином. Це відносно поширений спосіб надання значення за замовчуванням, якщо xйого немає.

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

function doSomething(data, callback) {
    callback = callback || function() {};
    // do stuff with data
    callback(); // callback will always exist
}

1

Це означає, що якщо xвстановлено, значення for zбуде x, інакше якщо yвстановлено, то його значення буде встановлено як zзначення s.

це те саме, що

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

Це можливо, тому що логічні оператори в JavaScript не повертають булевих значень, а значення останнього елемента, необхідного для завершення операції (в реченні АБО це було б перше неправдиве значення, у реченні AND було б останнє ). Якщо операція не вдалася, то falseповертається.


5
це неправильно! якщо (x) {z = x; } else {z = y;} якщо перше значення хибне, друге значення завжди присвоюється незалежно від того, яке значення є насправді.
злий

За винятком того, що я думаю, що він просто призначає y до z, якщо x є хибним . Ось так це працює для мене у FF, звичайно, що може залежати і від реалізації.
tvanfosson

7
Остання частина про повернення помилок не відповідає дійсності (каламбур не призначений). Якщо першим значенням є фальси, ||оператор просто повертає друге значення, незалежно від того, чи це правда чи ні.
Меттью Крамлі

-1. Ваш еквівалентний фрагмент коду є точним, але важливим моментом є те, що zвстановлюється значення, xякщо це значення є правдоподібним . В іншому випадку він встановлюється на значення y. Це означає, що якщо xвстановлено, наприклад, 0або порожній рядок "", це не робить те, що ви говорите, оскільки ці значення є хибними .
Даніель Кассіді

1

Його називають оператором короткого замикання.

Оцінка короткого замикання говорить, що другий аргумент виконується або оцінюється лише в тому випадку, якщо першого аргументу недостатньо для визначення значення виразу. коли перший аргумент функції АБО (||) оцінюється як істинне, загальне значення повинно бути істинним.

Він також може бути використаний для встановлення значення за замовчуванням для аргументу функції. "

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 не є нульовим, порожній рядок або 0 (логічне хибне), то він призначить його z. Якщо X дорівнює нулю, порожній рядок або 0 (логічно хибне), то він призначить y z.

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

Виведе "bob";


Ви повинні уточнити, що ви маєте на увазі під «порожнім». Порожні рядки примушують до 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;
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.