This is a collection of alternatives to using if
and other conditional block-statements in Javascript.
Some reasons to consider alternatives:
if
is usually a block-statement, and has a tendency to beget more block statements and nesting which make reading code more difficult- many
if
s can usually indicate you really need to break your functions down further, and that your function is doing too much at once - because it is a block-level element, the block can easily grow into something that clearly should be a function, or even many functions
- because
const
andlet
respectif
's blocks as scope boundaries, usingif
for assignment prevents you from assigningconst
within, and requires a blanklet
declaration outside of scope switch
statements can easily "leak" when missing abreak
statement, and create "branches" of code without braces, hiding these branches somewhat- each
if
creates a logical branch that cannot be independantly tested - if functional programming is something you want to do more of, you'll generally want less
if
Each example is a very basic form of a common conditional pattern. These suggestions are meant to give you some ideas of alternatives.
Some examples, such as the "incongruent if
/else
" examples could be considered antipatterns and are themselves likely to be considered by most as poorly-formed if
statements.
let y;
if (x === 1) {
y = 2;
}
Basic, returns y
as false
for default.
// ternary with false default
const y = x === 1 ? 2 : false;
Similar, but has a reusable function that can be memoized.
// functional ternary
const checkX = x => (x === 1 ? 2 : false);
const y = checkX(x);
let y;
if (x === 1) {
y = 2;
} else {
y = 1;
}
Very basic, readable. Allows y
to be a const
.
// ternary assignment
const y = x === 1 ? 2 : 1;
Very similar, but reusable and memoizable.
// functional ternary
const checkX = x => (x === 1 ? 2 : 1);
const y = checkX(x);
let y;
if (x === 1) {
y = 2;
} else if (x === 2) {
y = 17.3;
} else {
y = 1;
}
switch (x) {
case 1:
y = 2;
break;
case 2:
y = 17.3;
break;
default:
y = 1;
}
Use an object to store values as key-value pairs.
// key-value pair list with default
const vals = {
1: 2,
2: 17.3,
default: 1
};
const y = x in vals ? vals[x] : vals.default;
Very similar, and prevents namespace collisions: use a symbol for your default value key. (The example above with default
key would likely misbehave if x
ever had a value called default
.)
// key-value pair list with symbol
// this avoids namespace collision in key names
const $$defaultSymbol = Symbol();
const vals = {
1: 2,
2: 17.3,
[$$defaultSymbol]: 1
};
const y = x in vals ? vals[x] : vals[$$defaultSymbol];
Or, use an array to store the values. Works only if dealing with low numbers.
// value list accessed by index
const vals = [null, 2, 17.3];
const y = vals[x] || 1;
Use a getter inside a constant function expression as a means of looking up a key-value pair. Allows for memoization, though uses the dreaded this
keyword...
// getter
const vals = x => ({
1: 2,
2: 17.3,
get value() {
return this[x] || 1;
}
});
const y = vals(x).value;
Constant function expression returning a lookup table: zeroth index is the condition, first index is the result.
// conditionals table
const condTable = x => [
[x === 1, 2],
[x === 2, 17.3],
[true, 1]
];
const y = condTable(x)
.find(a => (a[0] ? a[1] : false))
.pop();
let y;
if (x === 1) {
y = 2;
} else if (x === 2) {
y = 17.3;
} else if (z === 4) {
y = 19.2;
{ else if (w === 'something') {
y = 22;
} else {
y = 1;
}
Another constant function expression returning a lookup table, this time taking an array as a parameter. The array param allows for memoization of the table.
// conditionals table with array param
// array-based param allows for memoization if need be
const condTable = ([x, z, w]) => [
[x === 1, 2],
[x === 2, 17.3],
[z === 4, 19.2],
[w === 'something', 22],
[true, 1]
];
const y = condTable([x, z, w])
.find(a => (a[0] ? a[1] : false))
.pop();
What if not just the conditions are mixed, but the results too?
let y;
let j;
if (x === 1) {
y = 2;
} else if (x === 2) {
y = 17.3;
} else if (z === 4) {
y = 19.2;
{ else if (w === 'something') {
y = 22;
{ else if (w === 'something else') {
j = 5;
} else {
y = 1;
}
Similar to conditionals table, use the table to execute functions rather than assign value.
// results are functions, pass everything into params that might be needed
let y, j;
const setY = v => y = v;
const setJ = v => j = v;
const condTable = ([x, z, w]) => [
[x === 1, setY, 2],
[x === 2, setY, 17.3],
[z === 4, setY, 19.2],
[w === 'something', setY, 22],
[w === 'something else', setJ, 5],
[true, setY, 1]
];
condTable([x, z, w])
.find(a => (a[0] ? a[1](a[2]) : false));