When we use JavaScript, we often write a lot of conditional statements. Here are five tips to help you write cleaner, prettier conditional sentences.
Use array.includes to handle multiple conditions
Here’s an example:
// Conditional statement
functiontest(fruit){
if(fruit==’apple’||fruit==’strawberry’){
console.log(‘red’);
}
}
At first glance, this doesn’t seem like a big deal.
However, what if we wanted to match more red fruits, like “cherries” and “cranberries”? If we need more | | to extend this statement?
Img1.sycdn.imooc.com/5f7340b8000…
We can rewrite the above conditional using array. includes.
functiontest(fruit){
// Extract the condition into the array
constredFruits=[‘apple’,’strawberry’,’cherry’,’cranberries’];
if(redFruits.includes(fruit)){
console.log(‘red’);
}
}
We extract all the red fruits into an array, which makes our code look cleaner.
Write less nesting and return sooner
Let’s add two conditions to the previous example:
If no fruit is provided, an error is thrown;
If the number of fruits is greater than 10, print it out.
functiontest(fruit,quantity){
constredFruits=[‘apple’,’strawberry’,’cherry’,’cranberries’];
// Condition 1: fruit must have a value
if(fruit){
// Condition 2: must be red
if(redFruits.includes(fruit)){
console.log(‘red’);
// Condition 3: Must be abundant
if(quantity>10){
console.log(‘bigquantity’);
}
}
}else{
thrownewError(‘Nofruit! ‘);
}
}
// Test results
test(null); // Error: Nofruits
test(‘apple’); // Print: red
test(‘apple’,20); // Print: red, bigquantity
Let’s take a closer look at the code above. We have:
1 if/else statement to filter invalid conditions;
Layer 3 nesting of if statements (conditions 1,2 &3).
JavaScript (” JS “for short) is a function-first, lightweight, interpreted or just-in-time compiled high-level programming language. Although it is best known as a scripting language for developing Web pages, it is also used in many non-browser environments. JavaScript is a prototype-based, multi-paradigm dynamic scripting language, and supports object-oriented, imperative, and declarative (such as functional) programming styles.
Personally, a general rule I follow is to return as early as possible when an invalid condition is found.
Return as soon as an invalid condition is found
functiontest(fruit,quantity){
constredFruits=[‘apple’,’strawberry’,’cherry’,’cranberries’];
// Condition 1: Throw an error early
if(! fruit)thrownewError(‘Nofruit! ‘);
// Condition 2: must be red
if(redFruits.includes(fruit)){
console.log(‘red’);
// Condition 3: Must be abundant
if(quantity>10){
console.log(‘bigquantity’);
}
}
}
This way, we have one less layer of nesting. This is a good code style, especially if the if statement is very long (if you have to scroll to the bottom to see if there is an else statement, which is a bit annoying).
If we reverse the conditions, we can further reduce the nesting level. Notice the following condition 2 statement to see how this is done:
Return as soon as an invalid condition is found
functiontest(fruit,quantity){
constredFruits=[‘apple’,’strawberry’,’cherry’,’cranberries’];
if(! fruit)thrownewError(‘Nofruit! ‘); // Condition 1: Throw an error early
if(! redFruits.includes(fruit))return; // Condition 2: Return fruit is not red
console.log(‘red’);
// Condition 3: Must be abundant
if(quantity>10){
console.log(‘bigquantity’);
}
}
By reversing the conditions of condition 2, our code is now unnested.
This technique is useful when our code has a long logical chain and we want to stop executing subsequent processes if a condition is not met.
However, there are no hard and fast rules that require you to do this. It’s up to you. Is this version of the code (no nesting) better and more readable to you than the previous version (nesting condition 2)?
I would have chosen the previous version (condition 2 is nested). Here’s why:
This code is short and straightforward, and a nested IF makes the structure clearer;
Conditional reverse transfer leads to more thought processes (increased cognitive burden).
So always aim for less nesting and earlier return, but don’t overdo it.
For those interested, here’s an article on the subject and a discussion on StackOverflow:
AvoidElse,ReturnEarlybyTimOxley
StackOverflowdiscussiononif/elsecodingstyle
Use function default arguments and destructions
I’m guessing you’re familiar with the following code: in JavaScript we often need to check for null/undefined and assign the default value:
functiontest(fruit,quantity){
if(! fruit)return;
constq=quantity||1; // If no quantity is provided, the default is 1
console.log(Wehave${q}${fruit}!) ;
}
// Test results
test(‘banana’); //Wehave1banana!
test(‘apple’,2); //Wehave2apple!
In fact, we can remove the variable q by passing the function’s default argument.
Functiontest (fruit,quantity=1){// Default to 1 if no quantity is provided
if(! fruit)return;
console.log(Wehave${quantity}${fruit}!) ;
}
// Test results
test(‘banana’); //Wehave1banana!
test(‘apple’,2); //Wehave2apple!
Is it simpler and more straightforward?
Note that all function arguments can have default values. For example, we could also give fruit a default value:
Functiontest (fruit = ‘unknown’, quantity = 1).
So what if fruit is an Object? Can we still use the default parameters?
functiontest(fruit){
// If there is a value, print it out
if(fruit&&fruit.name){
console.log(fruit.name);
}else{
console.log(‘unknown’);
}
}
// Test results
test(undefined); //unknown
test({}); //unknown
test({name:’apple’,color:’red’}); //apple
Looking at the example above, we want to print the fruit name attribute when it exists, otherwise print “unknown”.
We can avoid the fruit&&fruit.name condition by default arguments and destructing assignments.
// Destruct — just get the name attribute
// The default argument is empty object {}
functiontest({name}={}){
console.log(name||’unknown’);
}
// Test results
test(undefined); //unknown
test({}); //unknown
test({name:’apple’,color:’red’}); //apple
Since we only need the fruit name attribute, we can use {name} to deconstruct it, and then we can replace fruit.name with the name variable in our code.
We also use {} as its default value.
If we don’t do this,
In carrying out the test (undefined), you will get an error Cannotdestructurepropertynameof “undefined” or “null”.,
Because undefined doesn’t have a name attribute on it.
(Translator’s note: This is not accurate because destructuring only applies to objects, not because undefined has no name attribute.) Refer to deconstruction assignment -MDN)
If you don’t mind using a third-party library, there are ways to help reduce null checking:
Use the Lodashget function;
Use Facebook’s open source IDX library (with Babeljs).
Here’s an example using Lodash:
// Use the _ method provided by the Lodash library
functiontest(fruit){
// Get the value of attribute name, if not, set to the default value unknown
console.log(_.get(fruit,’name’,’unknown’);
}
// Test results
test(undefined); //unknown
test({}); //unknown
test({name:’apple’,color:’red’}); //apple
You can run the demo code here.
Alternatively, if you prefer functional programming (FP), you can choose to use Lodashfp — the functional version of Lodash (method name changed to get or getOr).
Map/Object may be a better choice than Switch
Let’s look at the following example where we want to print various fruits according to color:
functiontest(color){
// Use switchCase to find the corresponding fruit color
switch(color){
case’red’:
return[‘apple’,’strawberry’];
case’yellow’:
return[‘banana’,’pineapple’];
case’purple’:
return[‘grape’,’plum’];
default:
return[];
}
}
// Test results
test(null); / / []
test(‘yellow’); //[‘banana’,’pineapple’]
The code above doesn’t look wrong, but personally, it looks wordy. The same result can be achieved with object literals, and the syntax is much cleaner:
// Use the object literal to find the fruit of the corresponding color
constfruitColor={
red:[‘apple’,’strawberry’],
yellow:[‘banana’,’pineapple’],
purple:[‘grape’,’plum’]
};
functiontest(color){
returnfruitColor[color]||[];
}
Alternatively, you can use Map to achieve the same effect:
// Use the Map to find the fruit with the corresponding color
constfruitColor=newMap()
.set(‘red’,[‘apple’,’strawberry’])
.set(‘yellow’,[‘banana’,’pineapple’])
.set(‘purple’,[‘grape’,’plum’]);
functiontest(color){
returnfruitColor.get(color)||[];
}
Map is a new object type introduced in ES2015 that allows you to store key-value pairs.
Does that mean we should ban switch statements? Don’t limit yourself.
I myself use object literals whenever possible, but that doesn’t mean I don’t use the switch, it depends on the situation.
ToddMotto has a great article on switch statements and object literals that you might want to check out.
Lazy edition: Refactor syntax
For the example above, we can actually achieve the same effect using array.filter by refactoring our code.
constfruits=[
{name:’apple’,color:’red’},
{name:’strawberry’,color:’red’},
{name:’banana’,color:’yellow’},
{name:’pineapple’,color:’yellow’},
{name:’grape’,color:’purple’},
{name:’plum’,color:’purple’}
];
functiontest(color){
// Use Arrayfilter to find the fruit with the corresponding color
returnfruits.filter(f=>f.color==color);
}
There is always more than one way to solve a problem. For this example we have shown four implementations.
Codingisfun!
Use array. every and array. some to handle all/part of the condition
The last tip is more about using new (and not so new) JavaScript array functions to reduce the number of lines of code.
Looking at the following code, we want to check if all fruit is red:
constfruits=[
{name:’apple’,color:’red’},
{name:’banana’,color:’yellow’},
{name:’grape’,color:’purple’}
];
functiontest(){
letisAllRed=true;
// Condition: All fruit must be red
for(letfoffruits){
if(! isAllRed)break;
isAllRed=(f.color==’red’);
}
console.log(isAllRed); //false
}
This code is too long! We can reduce the code by array.every
constfruits=[
{name:’apple’,color:’red’},
{name:’banana’,color:’yellow’},
{name:’grape’,color:’purple’}
];
functiontest(){
Condition :(short form) all fruit must be red
constisAllRed=fruits.every(f=>f.color==’red’);
console.log(isAllRed); //false
}
Much clearer, right?
Similarly, if we want to check if at least one fruit is red, we can use array.some in a single line of code.
constfruits=[
{name:’apple’,color:’red’},
{name:’banana’,color:’yellow’},
{name:’grape’,color:’purple’}
];
functiontest(){
// Condition: at least one fruit is red
constisAnyRed=fruits.some(f=>f.color==’red’);
console.log(isAnyRed); //true
}