Like any kind of apps, JavaScript apps also have to be written well. Otherwise, we run into all kinds of issues later on.
In this article, we’ll look at some best practices for working with arrow functions. Also, we look at why and how to avoid mutations.
Don’t Use Getters and Setters
Since getters and setters introduce side effects, we shouldn’t use them if we want to make functions that are easy to work with.
For instance, we shouldn’t write:
const person = {
name: 'james',
get age() {
return this._age;
},
set age(n) {
if (n < 0) {
this._age = 0;
} else if (n > 200) {
this._age = 200;
} else {
this._age = n;
}
}
};
Also, we should call __defineGetter__
or __defineSetter__
:
person.__defineGetter__('name', () => {
return this.name || 'james';
});
or:
person.__defineSetter__('name', (name) => {
this.name = name.trim();
});
Instead, we create pure functions:
const restrictAge = (n, min, max) => {
if (n <= min) {
return min;
}
if (n >= max) {
return max;
}
return n;
}
const setAge = (age, person) => {
return Object.assign({}, person, { age: restrictAge(age, 0, 120 )});
}
This way, we have all pure functions, and it’s more versatile than our setter implementation since we can change the min
and max
.
Reduce the Use of Loops
Most loops in JavaScript code can be rewritten with array methods.
This is because there are already many methods like map
, filter
, and reduce
methods that can let us work with arrays in a shorter way.
Therefore, instead of writing:
for (const a of arr) {
result.push(a * 10);
}
We can write:
const result = arr.map(a => a * 10);
It’s much easier to use map
then a for-of loop.
Don’t Use Object.assign with a Variable as the First Argument
Object.assign
mutates the first argument.
Therefore, to use it in a way that doesn’t mutate existing data, we should pass in an empty object as the first argument.
Instead of writing:
const a = { foo: 1, bar: 2 };
const b = { baz: 3 };
Object.assign(a, b);
we write:
const c = Object.assign({}, a, b);
Reduce the Use of Mutating Array Methods
Since we have the spread operator and alternative array methods, we can avoid the use of mutating array methods in our code.
For example, instead of writing:
arr.pop();
We can write:
const arr = arr.slice(0, arr.length - 1);
The only one that’s harder to replace is sort
. In this case, we can make a copy of it with the spread operator and then call sort
on that array.
So we can write:
const copy = [...arr];
copy.sort();
Instead of splice
, we may consider using filter
or slice
.
If we want to remove one element with the given index, we can write:
const removed = arr.filter((a, i) => i !== index);
Then we remove the item at index index
and assigned it to a new variable.
Reduce the Use of Mutating Operators
It’s easy to mutate data accidentally.
So we may want to avoid using mutating operators.
For instance, we may want to avoid using the +=
, /=
, -=
, %=
or *=
operators.
Instead, we can use non-mutating array methods or assigning values to a new variable.
We can write:
const b = a + 10;
instead of:
a += 10;
If we have to do update a variable with new values by adding repeatedly, we can use reduce
:
const total = arr.reduce((total, a) => total + a, 0);
Before Careful with null or undefined
We should always be careful with null
or undefined
values since they are a source of many errors.
Therefore, we should make sure a variable or property isn’t null
or undefined
before working with them.
To check both, we can write:
if (val === null || val === undefined){
//...
}
or:
if (val === null || typeof val === 'undefined'){
//...
}
Conclusion
We should avoid mutating data so that we won’t accidentally change them.
It’s also easier to test function that doesn’t mutate data.
Also, we should be careful of null
or undefined
.