Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62
Subscribe to my email list now at http://jauyeung.net/subscribe/
We can clean up our JavaScript code so that we can work with them more easily.
In this article, we’ll look at some refactoring ideas that are relevant for cleaning up JavaScript classes and objects.
Replace Magic Number with Symbolic Constant
If we have lots of repeating values that have the same meaning but not explicitly stated, then we should turn them to a constant so everyone knows what they mean and we only have to change one place if a change is needed.
For instance, instead of writing:
const getWeight = (mass) => mass * 9.81
const potentialEnergy = (mass, height) => mass * height * 9.81
We write:
const GRAVITATIONAL_CONSTANT = 9.81;
const getWeight = (mass) => mass * GRAVITATIONAL_CONSTANT
const potentialEnergy = (mass, height) => mass * height * GRAVITATIONAL_CONSTANT
Now we know that 9.81 actually means GRAVITATIONAL_CONSTANT
and we don’t have to repeat ourselves.
Encapsulate Field
We can add getters and setters to a field to access a class field instead of manipulating it directly.
For instance, instead of writing:
class Person {
constructor(name) {
this.name = name;
}
}
We write:
class Person {
constructor(name) {
this._name = _name;
}
get name() {
return this._name;
}
set name(name) {
this._name = name;
}
}
This way, we can control how values are set since we can put code in our setter to set the name
.
We can also control who can get the name since it’s returned in the getter.
Encapsulate Collection
We can also encapsulate arrays that are part of the class instance.
For instance, instead of writing:
class Person {
constructor(friends) {
this._friends = _friends;
}
get friends() {
return this._friends;
}
set friends(friends) {
this._friends = friends;
}
}
const person = new Person(['joe', 'jane'])
In the code above, we have an array instead of another type of object as we have above.
But the idea is the same.
Replace Record With Data Class
We can replace a field with its own data class so that we can have more flexibility in the data we record.
For instance, instead of writing:
class Person {
constructor(name, bloodGroup) {
this.name = name;
this.bloodGroup = bloodGroup;
}
}
const person = new Person('joe', 'a')
We write:
class BloodGroup {
constructor(name) {
this.bloodGroup = name;
}
}
class Person {
constructor(name, bloodGroup) {
this.name = name;
this.bloodGroup = bloodGroup;
}
}
const bloodGroup = new BloodGroup('a');
const person = new Person('joe', bloodGroup)
This way, we can store more varieties of data in the bloodGroup
field.
Replace Type Code With State/Strategy
Instead of having a type field in the class, we create subclasses based on the type of object.
This way, we can have more members which both classes don’t share in their own subclass.
For instance, instead of writing:
class Animal {
constructor(type) {
this.type = type;
}
}
const cat = new Animal('cat');
const dog = new Animal('dog');
We write:
class Animal {
//...
}
class Cat extends Animal {
//...
}
class Dog extends Animal {
//...
}
const cat = new Cat();
const dog = new Dog();
In the examples above, instead of writing one Animal
class, we have Cat
and Dog
classes that are subclasses of the Animal
class.
This way, we can have members that they don’t share in their own class and the members that they share remain in the Animal
class.
Decompose Conditional
We can break up long conditional expressions into smaller conditional expressions that are named.
For instance, instead of writing:
let ieIEMac = navigator.userAgent.toLowerCase().includes("mac") && navigator.userAgent.toLowerCase().includes("ie")
We write:
let userAgent = navigator.userAgent.toLowerCase();
let isMac = userAgent.includes("mac");
let isIE = userAgent.toLowerCase().includes("ie");
let isMacIE = isMac && isIE;
We broke the conditional expressions by assigning smaller expressions into their own variables and then combining them to make everything easier to read.
Conclusion
If we have magic numbers, we should replace them with named constants.
We should add getters and setter methods for class fields so that we can control how they’re returned or set.
If we have type
fields, we can replace them with their own subclasses.
Finally, we can break up long conditional expressions into smaller ones so that it’s easier to read and understand.