Let's Talk About Method Chaining

Feb 07, 2017

If you've used jQuery, there is a high chance you've found yourself chaining methods. What does this look like? Let's have a look:

$('#element').css('color', 'tomato').find('.inner-element').css('color', 'rebeccapurple');

Basically, what is happening here is we're selecting #element, changing the color to tomato (a reddish orange color), then selecting all elements within it that have the class .inner-element and changing the color of those to rebeccapurple (purple). Without method chaining, this would instead look something like this:

const $element = $('#element');
const $innerElement = $('#element .inner-element');

$element.css('color', 'tomato');
$innerElement.css('color', 'rebeccapurple');

Still gets the job done, and arguably I prefer a subset of the second version, but this example just shows the difference between chaining and not chaining.

Honestly, I'm not really interested in talking about what chaining is; I'm more interested in talking about how chaining works. So let's get into it.

Setting up your code to be able to take advantage of method chaining is actually simpler than it may seem. First, let me explain what a method is. A method is simply a function that is attached to an object. Literally the same thing as a function, but when it is a property of an object the term is method. This is important to remember. Secondly, it is important to understand the context of the this keyword when using objects and methods. For the purposes of this article, just think of the this keyword as a reference to the object you are calling your method on. For a more technical definition checkout out this. See what I did there?

Now that you have an understanding of those 2 things, let's look into how you can implement chaining in your own code. Simply put, to enable method chaining all you have to do is return (from your methods) a reference to object they are being called on. That would be the this keyword. You know I love examples, so here one comes. First let's create the object we'll be working with. There are a few ways to make objects in JavaScript, but I'll just simply go with object literal syntax.

const employee = {
  id: 123456,
  firstName: 'Sam',
  lastName: 'Webb',
  title: 'JavaScript Ninja',
  
  sayName: function() {
    console.log(this.firstName);
    return this;
  },

  sayFullName: function() {
    console.log(`${this.firstName} ${this.lastName}`);
    return this;
  },

  screamName: function() {
    console.error(`${this.firstName.toUpperCase()}!`);
    return this;
  }
  
};

Check out the methods sayName, sayFullName, and screamName. Notice that they all return the this keyword. Again, in the current context this is just a reference to the entire employee object. Wherever you see return this, think of it as if you're reading return employee. Since we're returning the employee object, that means we can immediately use it again after calling one of the methods. Example time:

// Calling all 3 methods separately

employee.sayName();
// > Sam

employee.sayFullName();
// > Sam Webb

employee.screamName();
// > SAM!


// Calling all 3 using method chaining
employee.sayName().sayFullName().screamName();
// > Sam
// > Sam Webb
// > SAM!

Should I give a more practical example??? I'll give a more practical example:

const events = {
  eventListeners: {},

  addEventListener(event, fn) {
    this.eventListeners[event] = this.eventListeners[event] || [];
    this.eventListeners[event].push(fn);
    return this;
  },

  dispatchEvent(event) {
    const len = this.eventListeners[event].length;
    if (len) {
      for (let i = 0; i < len; i++) {
        this.eventListeners[event][i]();
      }
    }
    return this;
  }
};

events.addEventListener('oneventadd', function() {console.log('Event Added!')});

events.addEventListener('speak', function() {console.log('Hello')})
      .dispatchEvent('oneventadd')
      .addEventListener('speak', function() {console.log('Goodbye')})
      .dispatchEvent('oneventadd');
// > Event Added!
// > Event Added!

events.dispatchEvent('speak');
// > Hello
// > Goodbye

Hmm maybe not as practical as I thought, but with this example you should be able to have a clearer picture of how one could use method chaining in some pretty interesting ways. As always, I am not the God of JavaScript (that's Brandon Eich), I'm just a guy who read some stuff and remembered it. If I am remembering incorrectly, please feel free to let me know. I'm always glad to hear feedback that will make me more knowledgable.

Further Reading

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects