Выражение внутри оператора case switch

Я пытаюсь создать оператор switch, но, похоже, не могу использовать выражение, которое оценивается (а не заданную строку / целое число). Я могу легко сделать это с помощью операторов if, но, надеюсь, case будет быстрее.

Я пробую следующее

function reward(amount) {
    var $reward = $("#reward");
    switch (amount) {
        case (amount >= 7500 && amount < 10000):
            $reward.text("Play Station 3");
            break;
        case (amount >= 10000 && amount < 15000):
            $reward.text("XBOX 360");
            break;
        case (amount >= 15000):
            $reward.text("iMac");
            break;
        default:
            $reward.text("No reward");
            break;
    }
}

Я упустил что-то очевидное или это невозможно? Google не проявил дружелюбия в этом случае.

Любая помощь / указатели приветствуются

M


person Marko    schedule 12.08.2010    source источник


Ответы (8)


Вы всегда могли сделать

switch (true) {
  case (amount >= 7500 && amount < 10000):
    //code
    break;
  case (amount >= 10000 && amount < 15000):
    //code
    break;

  //etc...

Это работает, потому что true является константой, поэтому будет выполнен код под первым оператором case с выражением, которое оценивается как истинное.

Я думаю, это довольно "сложно", но я не вижу ничего плохого в его использовании. Простая инструкция if/else, вероятно, будет более лаконичной, и вам не придется беспокоиться о случайном провале. Но так или иначе.

person MooGoo    schedule 12.08.2010
comment
Это лучше в качестве ответа, чем ответ Дэниэлса. С небольшим предупреждением: все выражения перед тем, которое приводит к case true, также будут оценены. будь осторожен. - person ; 19.08.2013
comment
Да, это работает, потому что вы всегда можете думать о функции переключения как о таблице переходов, она должна только сопоставить и получить значение. Это не похоже на операторы if / else, потому что все if действительно нужно оценивать. Выше вы просите свой переключатель оценить против матча. Вот почему корпуса переключателей быстрее. - person Vontei; 25.01.2016
comment
@Vontei - со структурой if / else if / else они не все оцениваются, они оцениваются по очереди только до тех пор, пока определенное условие не станет истинным, что то же самое, что происходит с оценкой case выражения. - person nnnnnn; 12.10.2017

switch (true) @ MooGoo даст вам _ 2_ ошибка в jsLint, так что давайте проявим немного больше творчества на случай, если это проблема, и, я думаю, немного повысим читаемость.

Таким образом, мы не оцениваем, является ли каждый case true или false; мы сравниваем, равно ли значение этого case нашему switch члену. Итак, давайте воспользуемся этим, добавив сокращенное обозначение if в наш case оператор и вернув исходный член переключения, если условие истинно.

Я также привожу своего рода пример из реального мира, где вы хотите иметь два значения по умолчанию - одно, если ваш термин выходит за пределы вашего важного диапазона в положительном направлении, и другой, если вы находитесь в отрицательном направлении.

Ключевая фраза: case (x > 0 ? x : null):

Если мой член x больше нуля, верните x, чтобы x === x и я взяли ветвь case.

http://jsfiddle.net/rufwork/upGH6/1/

/*global document*/
/*jslint evil:true*/
var x = 10;

switch (x) {
    case (x > 0 ? x : null):
        document.write('ha ha ha!  I fooled switch AND jsLint!  Muhahahahaha!');
        break;
    case 0:
        document.write('zero is nothing.');
        break;
    case -1:
        document.write('low');
        break;
    case -2:
        document.write('lower');
        break;
    case -3: 
        document.write('lowest I care about');
        break;
    default: // anything lower than -3.
        document.write('TOO LOW!!!! (unless you cheated and didn\'t use an int)');
}
document.write('<br>done.');

Быстрый ответ на @ Sv443:

Обратите внимание, что переключатель default: говорит: если вы не обманули и не использовали int, и что для короткого замыкания требуется x === x при возврате x.

Но ваша точка зрения является полезным напоминанием о том, что NaN - это единственный случай, когда короткое замыкание неприменимо.

То есть x должен == x закоротить switch и, как сообщает нам MDN, NaN и только NaN будет сравнивать неравно самому себе (двойной или тройной =) .

Это также означает, что при включении значения NaNтолько значения NaN) всегда попадет default в ЛЮБОЙ switch, потому что вы не можете сопоставить его ценность.

Вот полная цитата из MDN :

NaN сравнивает неравное (через ==,! =, === и! ==) с любым другим значением, включая другое значение NaN. Используйте Number.isNaN (). или isNaN (), чтобы наиболее четко определить является ли значение NaN. Или выполните самосравнение: NaN и только NaN будут сравнивать себя неравно.

Вы можете изменить логику default, чтобы проверить, что у вас есть:

isNaN(x) ? document.write ('nan') : document.write('TOO LOW!!!! ...)');

Или вы даже можете стать хипстером, как предлагает MDN (но, пожалуйста, не надо; ^ D):

x !== x ? document.write ('nan') : document.write('TOO LOW!!!! ...)');

person ruffin    schedule 15.02.2013
comment
Это может ввести JSLint в заблуждение, но это более странно и труднее читать, чем то, на что JSLint жаловался в первую очередь. Если вы приняли сознательное решение использовать switch с выражениями в тех случаях, когда требуется OP и объяснил MooGoo, то вы не будете беспокоиться об этом конкретном предупреждении JSLint. - person nnnnnn; 12.10.2017
comment
@nnnnnn Что ж, я бы сказал, что switch (true) полностью антипаттерн, поскольку MooGoo по сути воссоздает if... else if... else. Моя основная мысль заключается в том, что этот вопрос заставляет меня думать об одном, возможно, практическом использовании диапазонов в переключателях - если вы попадаете в switch парадигму, которая по существу требует двух (+?) Значений по умолчанию. Приятно узнать, что вы можете использовать выражение в case, возвращать значение параметра switch и принудительно вызывать этот оператор. Но похоже, что вы голосуете за ответы @Daniel & @ FyodorSoikin так работает switch, и я действительно не могу с этим спорить;) - person ruffin; 12.10.2017
comment
Обратите внимание, что это не работает, когда x = NaN, поскольку внутренне JS предположительно выполняет проверку равенства, что невозможно с константой NaN (необходимо проверить с помощью Number.isNaN(), чего он не делает) - person Sv443; 07.06.2021
comment
@ Sv443 Адресовано в обновлении ответа. В этом случае NaN - буквально исключение, которое подтверждает (ну, единственное исключение) правило. - person ruffin; 07.06.2021
comment
Спасибо за редактирование этого 8-летнего ответа :) - person Sv443; 08.06.2021

Блок switch работает не так. case используется для хранения единственного значения, которое, если оно равно значению в строке switch. if-else утверждения сослужат вам хорошую службу.

Вот некоторая информация о блоке switch.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch

person Daniel A. White    schedule 12.08.2010
comment
Этот ответ по-прежнему неверен: проверьте спецификацию - обе ⟨expr1⟩ и ⟨expr2⟩ в switch( ⟨expr1⟩ ){ case ⟨expr2⟩ : break; } - это выражения. - person Sebastian Simon; 06.03.2021

Что ж, вы можете использовать выражения в операторе case, поэтому ваш переключатель не является синтаксической ошибкой. Но вы должны понимать, что Case Clause сравнивается с использованием === (строгое сравнение). Как только вы поймете, что значение должно точно совпадать со значением выражения в вашем switch(expression), вы можете поискать выражения в js.

Вызов функций - это выражения, поэтому давайте попробуем с ними:

function xbox(amount) { return amount >= 10000 && amount < 15000 && amount; }

function reward(amount) {
  var ps3 = function(amount) { return amount >= 7500 && amount < 10000 && amount; }

  function imac(amount) { return amount >= 15000 && amount; }

  var $reward = $("#reward");
  switch (amount) {
    case ps3(amount):
      $reward.text("Play Station 3");
      break;
    case xbox(amount):
      $reward.text("XBOX 360");
      break;
    case imac(amount):
      $reward.text("iMac");
      break;
    default:
      $reward.text("No reward");
      break;
  }
}
reward(8200)// -> Play Station 3
reward(11000)// -> XBOX 360
reward(20000)// -> iMac

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

Вот ваш фиксированный код:

function reward(amount) {
    var $reward = $("#reward");
    switch (amount) {
        case (amount >= 7500 && amount < 10000 && amount):
            $reward.text("Play Station 3");
            break;
        case (amount >= 10000 && amount < 15000 && amount):
            $reward.text("XBOX 360");
            break;
        case (amount >= 15000 && amount):
            $reward.text("iMac");
            break;
        default:
            $reward.text("No reward");
            break;
    }
}

Вот спецификация: https://tc39.github.io/ecma262/#sec-switch-statement Ссылка на es2016, потому что его легче искать, чем старый es3 pdf от 1999 года. Но он всегда работал так, но это малоизвестный факт.

Однако я сомневаюсь, что это быстрее, чем инструкции if. Если вы хотите, чтобы ваша пробежка выполнялась быстро, не прикасайтесь к модели DOM.

person dotnetCarpenter    schedule 13.12.2015

Вы также можете попробовать одну из моих любимых конструкций:

function reward(amount) {
    var $reward = $("#reward");
    $reward.text(
        (amount >= 7500 && amount < 10000) ?    "Play Station 3" :
        (amount >= 10000 && amount < 15000)?    "XBOX 360" :
        (amount >= 15000) ?                     "iMac" :
                                                "No reward"
    );
}
person robert    schedule 18.02.2014
comment
Вам все равно придется проверять, что сумма меньше числа, если вы оцениваете их в обратном порядке? - person Jim Jones; 26.06.2014
comment
Я думаю, конечно, это остановит людей с большим вознаграждением, если они получат их быстрее;) - person Jim Jones; 26.06.2014
comment
Вложенные тернарные операторы часто опасаются и полностью не одобряют. Поэтому лучше избегать их использования, если они не являются более простыми, очевидными и более простыми в обслуживании, чем альтернативные конструкции. Я предпочитаю тесты, которые работают в любом порядке. Как бы вы ни решили их писать, здесь простота и ремонтопригодность важнее эффективности. - person robert; 06.07.2014

Во-первых, switch работает не так. Вы должны указать константы для каждого case, и эти константы будут сравниваться с выражением в круглых скобках (в вашем случае amount). Вот как работает switch, точка.

Во-вторых, переключение происходит не быстрее нескольких ifсек.

И в-третьих, вам не стоит особо беспокоиться о незначительной оптимизации производительности, когда вы имеете дело с javascript.

person Fyodor Soikin    schedule 12.08.2010
comment
Что ж, всегда лучше беспокоиться о производительности. Это дает вам место, когда вам нужно сделать что-то, требующее высокой производительности. - person MindlessRanger; 18.08.2015
comment
Вы должны указывать константы для каждого случая - Нет, не можете. Использование констант - наиболее распространенный способ использования switch, но вы можете указать любое выражение в каждом case, с переменными или вызовами функций или без них, и значение будет сравниваться. - person nnnnnn; 12.10.2017
comment
Первый абзац неверен: проверьте спецификацию - оба ⟨expr1⟩ и ⟨expr2⟩ в switch( ⟨expr1⟩ ){ case ⟨expr2⟩ : break; } - это выражения. Два других абзаца больше подходят как комментарий, а не как ответ. - person Sebastian Simon; 06.03.2021

Проблема в том, что выражение переключения никогда не может совпадать с выражениями case, потому что выражение case будет иметь значение true или false, но выражение switch будет числом.

Решение, в котором для выражения switch установлено значение true, работает не потому, что true является константой, а потому, что фактически возможно равенство с выражениями case.

Неверно, что вам нужно указывать константы для каждого выражения case.

Чтобы подтвердить мой ответ, обратитесь к Дугласу Крокфорду, Javascript The Good Parts (2008), стр. 12:

Оператор switch выполняет многостороннее ветвление. Он сравнивает выражение на равенство со всеми выбранными случаями .... Когда обнаруживается точное совпадение, выполняются операторы соответствующего предложения case ... Предложение case содержит одно или несколько выражений case. Выражения case не обязательно должны быть константами.

person Jon Lyles    schedule 02.11.2011
comment
Можно добавить, что оценка блока case выполняется в порядке исходного кода. Сверху вниз. - person dotnetCarpenter; 13.12.2015

Мои 2 цента:

В идеале switch (как правило) должен оцениваться в одну ветвь case, тем самым достигая производительности O (1), и (кроме случаев пропадания) операторы case могут быть переупорядочены любым способом без изменения стратегии ветвления компилятора.

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

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

Пример:

var x = 6, factors = [];

switch(x){

    case (x%2 == 0): factors.push(2);
    break;

    case (x%3 == 0): factors.push(3);
    break;
    ....
 }

{Ожидайте комментариев по поводу некачественного кода}

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

Единственная часть состоит в том, что он «может» выглядеть более чистым для разработчика, но на самом деле может стать причиной нарушения работы в случае дополнительных условий.

person GNM    schedule 29.10.2015