I'm trying to create a switch statement but I can't seem to be able to use an expression that gets evaluated (rather than a set string/integer). I can easily do this with if statements but case should hopefully be faster.
I'm trying the following
function reward(amount) {
var $reward = $("#reward");
switch (amount) {
case (amount >= 7500 && amount < 10000):
$reward.text("Pla开发者_如何学Goy 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;
}
}
Am i missing something obvious or is this not possible? Google hasn't been friendly in this case.
Any help/pointers appreciated
M
amount
is a number, but the expressions in the case
clauses only evaluate to booleans; the values will never match.
You could always do
switch (true) {
case (amount >= 7500 && amount < 10000):
// Code
break;
case (amount >= 10000 && amount < 15000):
// Code
break;
// etc.
}
It works because the value being matched is now the boolean true
, so the code under the first case
clause with an expression that evaluates to true
will be executed.
It’s kinda “tricky”, I guess, but I see nothing wrong with using it. A simple if
–else
statement would probably be more concise, and you’d not have to worry about accidental fall-through. But there it is anyway.
@MooGoo's switch (true)
will give you a Weird condition
error in jsLint, so let's get a little more creative in case that's an issue, and, I think, increase readability a touch.
So we're not evaluating if each case
is true
or false
; we're comparing if that case
's value is equal to our switch
term. So let's take advantage of that by throwing a shorthand if
into our case
statement and return our original switch term if the condition's true.
I'm also including a sort of real world example, where you want to have two "defaults" -- one if your term is outside of your "important" range in the positive direction, and another in case you're in the negative direction.
Key phrase:
case (x > 0 ? x : null):
"If my term, x
, is greater than zero, return x
so that x === x
and I take the case branch."
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.');
Quick reply to @Sv443:
Do notice that the default:
switch says, "unless you cheated and didn't use an int" and that short circuiting requires x === x
when you return x
.
But your point is a useful reminder that NaN
is the only case where short circuiting can't apply.
That is, x
must == x
to short circuit in switch
and, as MDN tells us, "NaN, and only NaN, will compare unequal to itself" (double or triple =
).
That also means that switching on a NaN
value (and only a NaN
value) will always hit default
in ANY switch
because you can't match its value.
Here's the full quote from MDN:
NaN compares unequal (via ==, !=, ===, and !==) to any other value -- including to another NaN value. Use Number.isNaN() or isNaN() to most clearly determine whether a value is NaN. Or perform a self-comparison: NaN, and only NaN, will compare unequal to itself.
You could change the default
logic to check what you have:
isNaN(x) ? document.write ('nan') : document.write('TOO LOW!!!! ...)');
Or you even could go full hipster like MDN suggests (but please don't ;^D):
x !== x ? document.write ('nan') : document.write('TOO LOW!!!! ...)');
That is not how a switch
block works. The case
is used to hold a single value that if they are equal to the value on the switch
line. if-else
statements will serve you well.
Here is some information about the switch
block.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch
Well, you can have expressions in case
statement which is why your switch is not a syntax error. But you have to understand that the Case Clause is compared using === (strict comparison). Once you understand this, that the value must match exactly with the expression value in your switch(expression)
you can shop around for expressions in js.
Function calls are expressions, so let's try with them:
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
As you can see, you can both use function expressions and function definitions. It doesn't matter. Only that the expression in the case clause is an expression to evaluate. Which is the same as you did, only you did not return a value that was the same as the amount but rather a true or false value. In my example I return the exact amount if my condition is true, hence trigger the comparison to match.
Here is your fixed code:
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;
}
}
Here is the specification: https://tc39.github.io/ecma262/#sec-switch-statement The link is to es2016 because it's easier to lookup than the old es3 pdf from 1999. But it has always worked like this, but it is a little known fact.
I however doubt that this is faster than if
statements. If you want your run to run fast, then do not touch the DOM.
You could also try one of my favorite constructions:
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"
);
}
The problem is the switch expression cannot ever equal the case expressions, because the case expression will evaluate to true or false, but the switch expression will be a number.
The solution where the switch expression is set to true works not because true is a constant but because equality with the case expressions is actually possible.
It's not true that you have to specify constants for each case expression.
To back up my answer, refer to Douglas Crockford, Javascript The Good Parts (2008), page 12:
The switch statement performs a multiway branch. It compares the expression for equality with all the selected cases.... When an exact match is found, the statements of the matching case clause are executed... A case clause contains one or more case expressions. The case expressions need not be constants.
My 2 cents:
Ideally switch (as a principle) should evaluate to a single case branch , thereby achieving O(1) performance and (other than fall through cases) the case statements can be reordered in any way without changing the compiler branching strategy.
If expressions are used (assuming the language allows it), then theoretically, it can follow more than branch.
The compiler (other than those which can intelligently say what the developer is trying to do) will not be able to optimize the branching strategy statically and ideally thus losing its effectiveness.
Example:
var x = 6, factors = [];
switch(x){
case (x%2 == 0): factors.push(2);
break;
case (x%3 == 0): factors.push(3);
break;
....
}
{Expect comments on poor code}
In the above example, there is no practical way for the compiler to statically optimize, thus gains no performance benefit over if else.
The only part is that, it "may" look cleaner to the developer but could in-effect be cause for disruption in case of additional conditions.
Firstly, that's not how switch
works. You have to specify constants for each case
, and those constants will be compared to the expression in parentheses (in your case, amount
). That is how switch
works, period.
Secondly, switch is not faster than several if
s
And thirdly, you shouldn't really worry about miniscule performance optimizations when you're dealing with javascript.
精彩评论