JavaScript Refactoring — Objects and Values
Tags: codequality, webdev, programming, javascript
Published: 2021-01-25 03:21:40

**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.