There are a lot of conditional judgments to deal with when working in JavaScript, so here are five tips to help you write better/clearer conditional statements.
1. Use array.includes in multiple decisions
Let’s take a look at this example:
// condition
function test(fruit) {
if (fruit == 'apple' || fruit == 'strawberry') {
console.log('red'); }}Copy the code
At first glance, the above example looks ok. But what if more red fruits, such as cherry and cranberries, were added? You will use more | | to extend the conditional statement?
We can override the above conditional statement with array. includes(array.includes). As follows:
function test(fruit) {
// extract conditions to array
const redFruits = ['apple'.'strawberry'.'cherry'.'cranberries'];
if (redFruits.includes(fruit)) {
console.log('red'); }}Copy the code
We extract red fruits into an array. By doing so, the code looks cleaner.
2. Less nesting, early return
Let’s extend the above example to include two more conditions:
- If no fruit argument is passed, an error is thrown
- Accept the Quantity parameter and print it out if it exceeds 10
function test(fruit, quantity) {
const redFruits = ['apple'.'strawberry'.'cherry'.'cranberries'];
// condition 1: fruit must has value
if (fruit) {
// condition 2: must be red
if (redFruits.includes(fruit)) {
console.log('red');
// condition 3: must be big quantity
if (quantity > 10) {
console.log('big quantity'); }}}else {
throw new Error('No fruit! '); }}// test results
test(null); // error: No fruits
test('apple'); // print: red
test('apple'.20); // print: red, big quantity
Copy the code
Take a look at the code above and let’s go:
- 1 if/else statement to screen out invalid conditional statements
- Layer 3 nested statements (conditions 1,2 and 3)
The rule I personally follow is to return early when an invalid condition is found.
/ _return early when invalid conditions found _/
function test(fruit, quantity) {
const redFruits = ['apple'.'strawberry'.'cherry'.'cranberries'];
// condition 1: throw error early
if(! fruit)throw new Error('No fruit! ');
// condition 2: must be red
if (redFruits.includes(fruit)) {
console.log('red');
// condition 3: must be big quantity
if (quantity > 10) {
console.log('big quantity'); }}}Copy the code
By returning early, we reduce one layer of nested statements. This coding style is great, especially if you have a long if statement (imagine you have to scroll a long way to know there is an else statement, not cool at all).
(for the example above) we can further reduce if nesting by inverting judgment conditions and returning early. Let’s see how we deal with condition 2:
/ _return early when invalid conditions found _/
function test(fruit, quantity) {
const redFruits = ['apple'.'strawberry'.'cherry'.'cranberries'];
if(! fruit)throw new Error('No fruit! '); // condition 1: throw error early
if(! redFruits.includes(fruit))return; // condition 2: stop when fruit is not red
console.log('red');
// condition 3: must be big quantity
if (quantity > 10) {
console.log('big quantity'); }}Copy the code
By inverting condition 2, we avoid nested statements. This technique is useful when we are dealing with long logic and want to be able to stop when conditions are not met.
And it’s not that hard to do. Ask yourself, is this version (no conditional nesting) better/more readable than the previous version (two-layer nesting)?
However, for my part, I will keep the previous version (containing two layers of nesting). Because:
- The code is shorter and more direct, and the nesting of ifs is clearer
- Reversing judgment conditions may increase thinking load (increase cognitive load)
Therefore, nesting should be minimized and return early, but not too much. If you’re interested, you can check out the following article and StackOverflow discussion to learn more:
- Avoid Else, Return Early by Tim Oxley
- StackOverflow discussion on if/else coding style
3. Use default parameters and deconstruction
I’m guessing you’re familiar with the following code: in JavaScript we always need to check for null/undefined values and specify default values.
function test(fruit, quantity) {
if(! fruit)return;
const q = quantity || 1; // if quantity not provided, default to one
console.log(`We have ${q} ${fruit}! `);
}
//test results
test('banana'); // We have 1 banana!
test('apple'.2); // We have 2 apple!
Copy the code
In fact, we can eliminate the variable q by declaring the default function parameters.
function test(fruit, quantity = 1) { // if quantity not provided, default to one
if(! fruit)return;
console.log(`We have ${quantity} ${fruit}! `);
}
//test results
test('banana'); // We have 1 banana!
test('apple'.2); // We have 2 apple!
Copy the code
It’s easy and intuitive! Note that each declaration has its own default parameters. For example, we could also give fruit a default: function test(fruit = ‘unknown’, quantity = 1).
What if our fruit is an object? Can we assign a default parameter?
function test(fruit) {
// printing fruit name if value provided
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
Copy the code
In the above example, we print the fruit name if there is fruit name, otherwise unknown. We can avoid judging the condition fruit && fruit.name by setting default parameters and deconstruction.
// destructing - get name property only
// assign default empty object {}
function test({name} = {}) {
console.log (name || 'unknown');
}
//test results
test(undefined); // unknown
test({ }); // unknown
test({ name: 'apple'.color: 'red' }); // apple
Copy the code
Since we only need the name attribute, we can use {name} to deconstruct the parameter, and then we can use name instead of fruit.name.
We also declare an empty object {} as the default. If we don’t, you’ll get an error that can’t be deconstructed from undefined or NULL. Because undefined has no name property.
If you don’t mind using third-party libraries, there are ways to reduce null checking:
- Use the Lodash get function
- Facebook’s open source library IDX (used with Babeljs)
Here’s an example using Lodash:
// Include lodash library, you will get _
function test(fruit) {
console.log(__.get(fruit, 'name'.'unknown'); // get property name, if not available, assign default value 'unknown'
}
//test results
test(undefined); // unknown
test({ }); // unknown
test({ name: 'apple'.color: 'red' }); // apple
Copy the code
You can run the demo code in JSBIN, or if you’re a fan of functional programming, you can choose Lodash FP, the functional version of Lodash (change the method to get or getOr).
4. Favor object traversal over switch statements
Looking at the code below, we want to print fruit based on color.
function test(color) {
// use switch case to find fruits in 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']
Copy the code
The above code looks fine, but it’s somewhat redundant. The syntax looks much cleaner when you implement the same result with object literals:
// use object literal to find fruits in color
const fruitColor = {
red: ['apple'.'strawberry'].yellow: ['banana'.'pineapple'].purple: ['grape'.'plum']};function test(color) {
return fruitColor[color] || [];
}
Copy the code
Alternatively, you can use Map to achieve the same result:
// use Map to find fruits in color
const fruitColor = new Map()
.set('red'['apple'.'strawberry'])
.set('yellow'['banana'.'pineapple'])
.set('purple'['grape'.'plum']);
function test(color) {
return fruitColor.get(color) || [];
}
Copy the code
Map is an object type implemented after the ES2015 specification that allows you to store key-value pairs.
So, should we ban switch statements? Don’t limit yourself to doing this. Personally, I use object traversal whenever possible, but don’t stick to it strictly, and use a way that makes more sense for the current scene.
Todd Motto has an in-depth look at switch statements and traversal objects, which you can check out here.
TL; DL; Refactoring grammar
For the example above, we can achieve the same result by refactoring the code below with array.filter.
const fruits = [
{ name: 'apple'.color: 'red' },
{ name: 'strawberry'.color: 'red' },
{ name: 'banana'.color: 'yellow' },
{ name: 'pineapple'.color: 'yellow' },
{ name: 'grape'.color: 'purple' },
{ name: 'plum'.color: 'purple'}];function test(color) {
// use Array filter to find fruits in color
return fruits.filter(f= > f.color == color);
}
Copy the code
There are more than one way to achieve the same result, and we’ve shown four of them above. Coding is fun!
5. Use array.every/array.some for all/part judgments
The final tip is to use Javascript’s built-in array functions to reduce the number of lines of code. Looking at the code below, we want to see if all the fruit is red:
const fruits = [
{ name: 'apple'.color: 'red' },
{ name: 'banana'.color: 'yellow' },
{ name: 'grape'.color: 'purple'}];function test() {
let isAllRed = true;
// condition: all fruits must be red
for (let f of fruits) {
if(! isAllRed)break;
isAllRed = (f.color == 'red');
}
console.log(isAllRed); // false
}
Copy the code
The code above is too long! We use array.every to reduce the number of lines of code:
const fruits = [
{ name: 'apple'.color: 'red' },
{ name: 'banana'.color: 'yellow' },
{ name: 'grape'.color: 'purple'}];function test() {
// condition: short way, all fruits must be red
const isAllRed = fruits.every(f= > f.color == 'red');
console.log(isAllRed); // false
}
Copy the code
Much clearer, right? Similarly, we want to test if any fruit is red, which we can do using array.some.
const fruits = [
{ name: 'apple'.color: 'red' },
{ name: 'banana'.color: 'yellow' },
{ name: 'grape'.color: 'purple'}];function test() {
// condition: if any fruit is red
const isAnyRed = fruits.some(f= > f.color == 'red');
console.log(isAnyRed); // true
}
Copy the code
conclusion
Let’s write more readable code together. I hope you can learn something new from this article.
That’s all. Have fun coding!
The original pausing. IO/bar – talk / 5 -…
First article: github.com/reng99/blog…
More: github.com/reng99/blog…