The translation
Conditionals are often used when writing JavaScript code. Here are five tips to help you write better, more concise conditionals.
1. Use array. includes to process various conditions
Let’s look at an example:
// conditionfunction test(fruit) {
if (fruit == 'apple' || fruit == 'strawberry') {
console.log('red');
}}
Copy the code
At first glance, the above example seems fine. But what if we added more red fruits, like cherries and cranberries? That is about to use | | write more condition judgment. To solve this problem, we can use array.includes to override the above criteria. Here’s an example:
function test(fruit) {
// extract conditions to array
const redFruits = ['apple'.'strawberry'.'cherry'.'cranberries'];
if (redFruits.includes(fruit)) {
console.log('red');
}}
Copy the code
Extract the “red fruit” (condition) into the array, so it looks neat.
2. Reduce nesting and return early
Let’s expand on the previous example by adding two conditions:
- Throw an exception if there is no fruit
- If the quantity exceeds 10, the quantity of fruit is exported
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
Looking at the code above, you see the following problems:
- An if/else statement that filters out invalid conditions
- One general rule I personally follow is to return as early as possible when dealing with invalid conditions
/ _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 writing this way, you can reduce the nesting by one level. This is good when the if statement is long. (Imagine if you had to scroll very far down to see the else language, which would not be cool). We can also reduce the nesting of if statements by using the reverse condition and returning early, as shown in the second condition.
/ _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 taking the opposite condition in the second condition, the code now releases a nest. This works well when we have to write long logic and want to stop the process further. However, this writing is not hard and fast, it depends on the specific scene. Sometimes, you have to ask yourself whether this way of writing (no nesting) is better or more readable than the previous one (nesting in condition 2).
For me, I’m just going to stay away from the previous way of writing (nested in condition 2) for two reasons:
- This is shorter and more concise than nested if statements
- Inverted conditional judgments may lead to more thought processes (increased cognitive load)
Therefore, always aim to reduce nesting and return early in time, but don’t overdo it. If you’re interested, check out the following related article and more discussion on this topic in StackOverflow:
- Avoid else, return early
- StackOverflow discussion of if/else code styles
3. Use function default arguments and destruct
I’m guessing the following code looks familiar to you, but we still need to check for null values (undefined or null) when executing JavaScript.
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 assign default parameters to the function by eliminating the variable p. Here’s an example:
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
Doesn’t that look simple and intuitive? Remember that each argument has its own default function argument. For example, we can also assign a default value for fruit (the first argument in the above code) :
function test(fruit = 'unknown', quantity = 1)
Copy the code
Another question arises: What if the Fruit parameter is of type Object? Can we specify a default value?
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({}); // unknowntest({ name: 'apple', color: 'red' }); // apple
Copy the code
Looking at the example above, we want to print out the name of fruit if the value exists, otherwise print out “unknown”. We can avoid the fruit&& Fruit.name condition by checking the default function arguments and object destructuring. Take a look at these examples:
// destructing - get name property only
// assign default empty object {}
function test({name} = {}) {
console.log (name || 'unknown'); } / /test results
test(undefined); // unknown
test({}); // unknowntest({ name: 'apple', color: 'red' }); // apple
Copy the code
Since we only need the fruit object’s name property, we can use {name} to deconstruct the object, and then we can use name as a variable in our code instead of Fruit.name. We will also allocate an empty object {} as the default argument. If you don’t do this, you will get an error when executing the test(undefined) line: the ‘undefined’ or ‘null’ attribute name cannot be resolved because undefined has no name attribute. If you don’t mind using third-party libraries, there are several ways to reduce null checking.
- Use the Lodash get method
- Use Facebook’s open source IDX library (and Bablejs)
Here’s an example of 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({}); // unknowntest({ name: 'apple', color: 'red' }); // apple
Copy the code
You can run the demo here. Alternatively, if you are keen on using functional programming (FP), you may choose to use Lodash FP, a functional version of Lodash (with the method changed to get or getOr).
4. Support Map or object literals instead of switch declarations
Let’s look at the following example:
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 error-free, but I find it quite cluttered. You can achieve the same result with the cleaner syntax of literal objects. Such as:
// 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 a Map to achieve the same result, for example:
// 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 that has only been available since ES6 and can be used to store key-value pairs.
Use array. every and array. some for all or some rules
The final tip is about taking advantage of the new (actually not so new) JavaScript array methods to reduce code functions. Looking at the code below, we want to see if all fruits are 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 really lengthy! We can use array. every to reduce the number of lines of code. Take a look at:
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
Isn’t it much simpler now? Similarly, if you want to test for red fruit, use array. some in a single line of code.
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 work together to generate more readable code. I hope you learned something new in this article. That’s all. Happy coding! (after)
Afterword.
The above translation is only used for learning communication, the level is limited, there are inevitably mistakes, please correct me.
Read more
Deconstruct the assignment Map
The original link
The original