2 Reasons Why You Must Understand Delegate Prototypes
Tags: javascript, react, node, webdev
Published: 2019-07-25 04:35:08

Find me on [medium](https://medium.com/@jsmanifest)

I was reading a section in a book about JavaScript and I came across an issue (but also the power of the concept that the issue stems from) that I want to write about, especially for newcomers to JavaScript. And even if you aren't new, there's a chance you might not know about this issue in JavaScript.

This article will go over a known anti-pattern with delegate prototypes. To users of React, the concept of this anti-pattern might be more familiar to them. But we will also go over how you can use that concept to turn things around and greatly improve the performance of your apps as you can see being used in majority of the JavaScript libraries today!

So if you want to create a library in JavaScript or have any plans to, I highly recommend you to understand how you can optimize your app by understanding how you can take advantage of delegating prototypes to improve the performance of your app if you haven't understood them yet. There's a name for it called the [Flyweight Pattern](https://www.dofactory.com/javascript/flyweight-design-pattern) which will be explained in this article.

If you don't know what a prototype is, all prototypes are basically objects that JavaScript uses to model other objects after. You can say that it's similar to classes in ways that it can construct multiple instances of objects, but it's [also an object itself](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype).

In JavaScript, all objects have some internal reference to a delegate prototype. When objects are queried by property or method lookups, JavaScript first checks the current object, and if that doesn't exist, then it proceeds to check the object's prototype, which _is the delegate prototype_, and then proceeds with that prototype's prototype, and so on. When it reaches the end of the prototype chain the last stop ends at the root `Object` prototype. Creating objects attaches that root `Object` prototype at the root level. You can branch off objects with different immediate prototypes set with Object.create().

Let's take a look at the code snippet below:

```js
const makeSorceress = function(type) {
  return {
    type: type,
    hp: 100,
    setName(name) {
      this.name = name
    },
    name: '',
    castThunderstorm(target) {
      target.hp -= 90
    },
  }
}

const makeWarrior = function(type) {
  let battleCryInterval

  return {
    type: type,
    hp: 100,
    setName(name) {
      this.name = name
    },
    name: '',
    bash(target) {
      target.hp -= 10
      this.lastTargets.names.push(target.name)
    },
    battleCry() {
      this.hp += 60
      battleCryInterval = setInterval(() => {
        this.hp -= 1
      }, 1000)
      setTimeout(() => {
        if (battleCryInterval) {
          clearInterval(battleCryInterval)
        }
      }, 60000)
      return this
    },
    lastTargets: {
      names: [],
    },
  }
}

const knightWarrior = makeWarrior('knight')
const fireSorc = makeSorceress('fire')

const bob = Object.create(knightWarrior)
const joe = Object.create(knightWarrior)
const lucy = Object.create(fireSorc)

bob.setName('bob')
joe.setName('joe')
lucy.setName('lucy')

bob.bash(lucy)
```

We have two factory functions, one of them is `makeSorceress` which takes a `type` of sorceress as an argument and returns an object of the sorceress's abilities. The other factory function is `makeWarrior` which takes a `type` of warrior as an argument and returns an object of the warrior's abilities.

We instantiate a new instance of the warrior class with type `knight` along with a sorceress with type `fire`.

We then used `Object.create` to create new objects for bob, joe, and lucy, additionally delegating the prototype objects for each.

Bob, joe, and lucy were set with their names on the instance so that we claim and expect their own properties. And finally, bob attacks lucy with using `bash`, decreasing her HP by 10 points.

At a first glance, there doesn't seem to be anything wrong with this example. But there is actually a problem. We expect bob and joe to have their own copy of properties and methods, which is why we used `Object.create`. When bob bashes lucy and inserts the last targeted name into the `this.lastTargets.names` array, the array will include the new target's name.

We can log that out and see it for ourselves:

```js
console.log(bob.lastTargets.names)
// result: ["lucy"]
```

The behavior is expected, however when we _also log the last targeted names_ for `joe`, we see this:

```js
console.log(joe.lastTargets.names)
// result: ["lucy"]
```

This doesn't make sense, does it? The person attacking lucy was bob as clearly demonstrated above. But why was joe apparently involved in the act? The one line of code explicitly writes `bob.bash(lucy)`, and that's it.

So the problem is that bob and joe are actually sharing the _same state_!

But wait, that doesn't make any sense because we should have created their own separate copies when we used `Object.create`, or so we assumed.

Even the [docs at MDN explicitly says that the Object.create() method creates a _new object_](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create). It does create a new object--which it did, but the problem here is that if you mutate object or array properties on _prototype_ properties, the mutation will _leak_ and affect other instances that have some link to that prototype on the prototype chain. If you instead replace the _entire_ property on the prototype, the change _only_ occurs on the instance.

For example:

```js
const makeSorceress = function(type) {
  return {
    type: type,
    hp: 100,
    setName(name) {
      this.name = name
    },
    name: '',
    castThunderstorm(target) {
      target.hp -= 90
    },
  }
}

const makeWarrior = function(type) {
  let battleCryInterval

  return {
    type: type,
    hp: 100,
    setName(name) {
      this.name = name
    },
    name: '',
    bash(target) {
      target.hp -= 10
      this.lastTargets.names.push(target.name)
    },
    battleCry() {
      this.hp += 60
      battleCryInterval = setInterval(() => {
        this.hp -= 1
      }, 1000)
      setTimeout(() => {
        if (battleCryInterval) {
          clearInterval(battleCryInterval)
        }
      }, 60000)
      return this
    },
    lastTargets: {
      names: [],
    },
  }
}

const knightWarrior = makeWarrior('knight')
const fireSorc = makeSorceress('fire')

const bob = Object.create(knightWarrior)
const joe = Object.create(knightWarrior)
const lucy = Object.create(fireSorc)

bob.setName('bob')
joe.setName('joe')
lucy.setName('lucy')

bob.bash(lucy)
bob.lastTargets = {
  names: [],
}

console.log(bob.lastTargets.names) // result: []
console.log(joe.lastTargets.names) // result: ["lucy"]
```

If you change the `this.lastTargets.names` property, it will be reflected with other objects that are linked to the prototype. However, when you change the prototype's property (`this.lastTargets`), it will override that property _only for that instance_. To a new developer's point of view, this can become a little difficult to grasp.

Some of us who regularly develop apps using React have commonly dealt with this issue when managing state throughout our apps. But what we probably never paid attention to is how that concept stems through the JavaScript language itself. So to look at this more officially, it's a problem with the JavaScript language in itself that this an anti pattern.

But can't it be a good thing?

In certain ways it _can_ be a good thing because you can optimize your apps by delegating methods to preserve memory resources. After all, every object just needs _one copy_ of a method, and methods can just be shared throughout all the instances unless that instance needs to override it for additional functionality.

For example, let's look back at the `makeWarrior` function:

```js
const makeWarrior = function(type) {
  let battleCryInterval

  return {
    type: type,
    hp: 100,
    setName(name) {
      this.name = name
    },
    name: '',
    bash(target) {
      target.hp -= 10
      this.lastTargets.names.push(target.name)
    },
    battleCry() {
      this.hp += 60
      battleCryInterval = setInterval(() => {
        this.hp -= 1
      }, 1000)
      setTimeout(() => {
        if (battleCryInterval) {
          clearInterval(battleCryInterval)
        }
      }, 60000)
      return this
    },
    lastTargets: {
      names: [],
    },
  }
}
```

The `battleCry` function is probably safe to be shared throughout all prototypes since it doesn't depend on any conditions to function correctly, besides that it requires an `hp` property which is already set upon instantiation. Newly created instances of this function do not necessarily need their own copy of `battleCry` and can instead delegate to the prototype object that originally defined this method.

The anti pattern of sharing data between instances of the same prototype is that storing state is the biggest drawback, because it can become very easy to accidentally mutate shared properties or data that shouldn't be mutated, which has long been a common source of bugs for JavaScript applications.

We can see this practice being in use for a good reason actually, if we look at how the popular [request](https://github.com/request/request) package instantiates the `Har` function in [this source code](https://github.com/request/request/blob/master/lib/har.js):

```js
function Har(request) {
  this.request = request
}

Har.prototype.reducer = function(obj, pair) {
  // new property ?
  if (obj[pair.name] === undefined) {
    obj[pair.name] = pair.value
    return obj
  }

  // existing? convert to array
  var arr = [obj[pair.name], pair.value]

  obj[pair.name] = arr

  return obj
}
```

So why doesn't `Har.prototype.reducer` just get defined like this?

```js
function Har(request) {
  this.request = request

  this.reducer = function(obj, pair) {
    // new property ?
    if (obj[pair.name] === undefined) {
      obj[pair.name] = pair.value
      return obj
    }

    // existing? convert to array
    var arr = [obj[pair.name], pair.value]

    obj[pair.name] = arr

    return obj
  }
}
```

As explained previously, if newer instances were to be instantiated, it would actually degrade the performance of your apps since it would be [recreating new methods on each instantiation], which is the `reducer` function.

When we have separate instances of `Har`:

```js
const har1 = new Har(new Request())
const har2 = new Har(new Request())
const har3 = new Har(new Request())
const har4 = new Har(new Request())
const har5 = new Har(new Request())
```

We're actually creating _5 separate copies of `this.reducer` in memory_ because the method is defined in the instance level. If the reducer was defined directly on the prototype, multiple instances of `Har` will _delegate_ the `reducer` function to the method defined on the prototype! This is an example of how to take advantage of delegate prototypes and improve the performance of your apps.

## Conclusion

That's all I needed to say. I hope you learned something from this post, and see you next time!

Find me on [medium](https://medium.com/@jsmanifest)