JavaScript Refactoring — Objects and Values

John Au-Yeung - Jan 25 '21 - - Dev Community

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
Enter fullscreen mode Exit fullscreen mode

We write:

const GRAVITATIONAL_CONSTANT = 9.81;

const getWeight = (mass) => mass * GRAVITATIONAL_CONSTANT

const potentialEnergy = (mass, height) => mass * height * GRAVITATIONAL_CONSTANT
Enter fullscreen mode Exit fullscreen mode

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;
  }
}
Enter fullscreen mode Exit fullscreen mode

We write:

class Person {
  constructor(name) {
    this._name = _name;
  }

  get name() {
    return this._name;
  }

  set name(name) {
    this._name = name;
  }
}
Enter fullscreen mode Exit fullscreen mode

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'])
Enter fullscreen mode Exit fullscreen mode

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')
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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');
Enter fullscreen mode Exit fullscreen mode

We write:

class Animal {
  //...
}

class Cat extends Animal {
  //...
}

class Dog extends Animal {
  //...
}

const cat = new Cat();
const dog = new Dog();
Enter fullscreen mode Exit fullscreen mode

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")
Enter fullscreen mode Exit fullscreen mode

We write:

let userAgent = navigator.userAgent.toLowerCase();
let isMac = userAgent.includes("mac");
let isIE = userAgent.toLowerCase().includes("ie");
let isMacIE = isMac && isIE;
Enter fullscreen mode Exit fullscreen mode

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.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .