JavaScript Refactoring — Classes
Tags: codequality, webdev, javascript, programming
Published: 2021-01-25 03:19:39

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

### Inline Class

We start with 2 classes and roll them into one. It’s the reverse of the previous refactoring.

For instance, instead of writing:

```
class PhoneNumber {
  constructor(phoneNumber) {
    this.phoneNumber = phoneNumber;
  }
}

class Person {
  constructor(name, phoneNumber) {
    this.name = name;
    this.phoneNumber = new PhoneNumber(phoneNumber);
  }
}
```

We roll the code for the `PhoneNumber` class back into the `Person` class as follows:

```
class Person {
  constructor(name, phoneNumber) {
    this.name = name;
    this.phoneNumber = phoneNumber;
  }
}
```

We may want to do this if the class that we separated out isn’t that complex.

In the code above, we have the `PhoneNumber` class that has no methods, so we can just roll it into the `Person` class since `phoneNumber` is a property that a `Person` instance can have.

### Hide Delegate

We can create methods on the client class to hide the underlying implementation of the code by filling the method class with code that gets the value we want directly.

This reduces the coupling of our code for getting the item that we want.

For instance, instead of writing the following:

```
class Department {
  constructor(name, deptHead) {
    this.name = name;
    this.deptHead = deptHead;
  }

  getDeptHead() {
    return deptHead;
  }
}

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

  getDept() {
    return this.dept;
  }
}

const deptHead = new Person('jane');
const dept = new Department('customer service', deptHead);
const employee = new Person('joe', dept);
const manager = employee.getDept().getDeptHead();
```

which requires us to get the department first before getting the department head as we did on the last line.

We can write the following:

```
class Department {
  constructor(name, deptHead) {
    this.name = name;
    this.deptHead = deptHead;
  }
}

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

  getDept() {
    return this.dept;
  }

  getDeptHead() {
    return this.dept.deptHead;
  }
}

const deptHead = new Person('jane');
const dept = new Department('customer service', deptHead);
const employee = new Person('joe', dept);
const manager = employee.getDeptHead();
```

All we did was to add the `getDeptHead` method to `person` to get the department head from the department directly.

Then we don’t have to get the department first to get the department head, which reduces coupling between the `Person` and `Department` class.

### Introduce Foreign Method

We can add a foreign method to a class that we can’t modify directly by adding methods to it with `Object.assign` .

For instance, we can write the following:

```
class Foo {
  //...
}

const mixin = {
  bar() {
    //...
  }
}

Object.assign(Foo.prototype, mixin)
```

In the code above, we added the `bar` instance method to `Foo` by calling `Object.assign` , given the `Foo` is a class that we can’t modify.

### Introduce Local Extension

In addition to adding new methods to a class directly, we can create new class with extra methods that we want to call by making a class with the methods a subclass of the class that we can’t modify directly.

For instance, we can write the following:

```
class Foo {
  //...
}

class Bar extends Foo {
  bar() {
    //...
  }
}
```

In the code above, we created a subclass of `Foo` that has the methods that we want to call, where `Foo` is a class that we can’t change.

Now we don’t have to modify `Foo` by adding things to its prototype.

### Self Encapsulate Field

We can add getters and setters methods to a field that we access directly so that we can encapsulate it.

For example, instead of writing the following:

```
class Counter {
  inRange(arg) {
    return arg >= this._low && arg <= this._high;
  }
}
```

We write:

```
class Counter {
  inRange(arg) {
    return arg >= low() && arg <= high();
  }

  get low() {
    return this._low;
  }

  get high() {
    return this._high;
  }
}
```

This lets us encapsulate fields and we can also apply other operations to it without access to the `high` and `low` fields and applying operations to them directly.

### Conclusion

We can move members in classes that don’t do much into another class so that we can remove the class that doesn’t do much.

To reduce coupling between classes, we can methods to directly get what we want instead of having to do that in a roundabout manner.

We can encapsulate fields so that we can get a value and do something before it before the value we returned.