DEV Community

Cover image for Keep Your Methods Chained πŸ”—πŸ”—πŸ”—
Steve Griffith
Steve Griffith

Posted on

Keep Your Methods Chained πŸ”—πŸ”—πŸ”—

Chaining methods is something that you have likely done at some point and you may not have even realized it. If you haven't then you will definitely have seen it in other people's code. This is what it looks like when you chain methods.

let names = ['Tony', 'Adesh', 'Robert', 'Su Cheng'];
//want to run both filter and map on this array.
names = names.filter(someFunc).map(otherFunc);
Enter fullscreen mode Exit fullscreen mode

We will look at an expanded version of this and explain all the details below.

What is Chaining

The process of programming is simply sequential problem solving. We break a problem down into small logical steps and decide the best order to execute those steps.

By chaining our methods we can leverage best practices like pure functions. We can create our small logical steps with pure functions.

A pure function is one that will always provide the same output for the same input and it can't have any side effects - like changing the value of something outside the function that had nothing to do with the input or output values.

This article has a logical sequence to it. It is divided into sections. The sections are split into paragraphs. The paragraphs are split into sentences. The sentences are split into individual words. The sequence of each of those steps matters. Change the steps at any level and the tutorial fails. Just like your program.

Chaining your methods is a way that you can hard code, in a very readable way, the steps that you are taking.

let result = myobject.stepOne().stepTwo().stepThree();
Enter fullscreen mode Exit fullscreen mode

How Does Chaining Work

The ability to chain them does have some restrictions. The result of the first step must be an Object or datatype that is capable of initiating the next step.

JavaScript will automatically read and execute these steps from left to right.

When it completes the final step then the result will be returned and assigned to the waiting variable on the left side of the =, if there is one.

As long as you are returning a valid object type for the next method, then you can keep chaining.

Here is a simple example with a couple of built-in methods - Number.prototype.toFixed(), and Number.parseFloat().

let pie = '3.1415 is the approximate value of PI';
let num = parseFloat(pie).toFixed(2); 
Enter fullscreen mode Exit fullscreen mode

The value of num will be "3.14". The global method parseFloat accepts a string and extracts the left-most digits, decimal is permitted. It will return a Number.

The toFixed method can be run on a Number. It will use its argument and convert the Number to the String equivalent of the Number with the correct number of decimal places.

The longer way to write this would have been to add the first method on its own line and create another variable to hold the value of the middle step.

let pie = '3.1415 is the approximate value of PI';
let middle = parseFloat(pie);
let num = middle.toFixed(2); 
// the value of num will be "3.14"
Enter fullscreen mode Exit fullscreen mode

Chaining our methods saves that middle step. No creation of an extra variable. No extra line of code. We can just read the steps on a single line. πŸ’―

Can I Chain my own Methods

If you are building your own Object(s) then you can also chain your own object methods, as long as you follow the same rule - return the correct datatype to be used in the next method.

Here is an example with a Person πŸ§” object. (The object could be built with the class syntax too).

const Person = function(_name){
  this.name = _name;
}
Person.prototype.speak = function(_phrase){
  console.log(`${this.name} says, "${_phrase}"`);
}
Person.prototype.drink = function(_beverage){
  console.log(`${this.name} is enjoying a ${_beverage}`);
}
Enter fullscreen mode Exit fullscreen mode

With our Person object we can now instantiate a person and call the methods.

let rashad = new Person('Rashad');
rashad.speak('Do your homework');
rashad.drink('coffee');
Enter fullscreen mode Exit fullscreen mode

Everything works just fine. But we can't chain speak or drink. Both functions return the default undefined.

However, if we add a return statement and return our Person object that we instantiated...

Person.prototype.speak = function(_phrase){
  console.log(`${this.name} says, "${_phrase}"`);
  return this;
}
Person.prototype.drink = function(_beverage){
  console.log(`${this.name} is enjoying a ${_beverage}`);
  return this;
}
Enter fullscreen mode Exit fullscreen mode

We don't want to add a return to the constructor function because it has to return the newly created instance.

NOW we can chain our methods and it WILL work πŸ™Œ.

let vladimir = new Person('Vladimir');
vladimir.speak('I am thirsty').drink('beer');
Enter fullscreen mode Exit fullscreen mode

And just like that, you're almost ready to flex πŸ’ͺ.

Add Some Functions to the Mix

We have a couple simple methods on our Person πŸ§” object, but we are still using really simple code.

What if our methods internally called on other functions? What if they accepted other functions as a supplied argument? How do we keep chaining and keep our methods pure?

Let's add an eat method to our Person object. We could just pass a String to the method with the name of the food. However, passing in a function that will choose a food item for us from some other source is a more practical and realistic way to do this.

...or why not both?

Why not both?

First, we add the new eat method to our Person πŸ§” object.

Person.prototype.eat = function(source){
  let food = '';
  switch(typeof source){
    case 'string':
      food = source;
      break;
    case 'function':
      food = source();
      break;
  }
  console.log(`${this.name} is eating ${food}`);
  return this;
}
Enter fullscreen mode Exit fullscreen mode

Now our method can accept either a Function to call to get the food OR a String with the name of the food.

It can also be chained because it returns some Object that has other methods which could be called.

Here is an example of a food function that could be used.

const food = function(){
  let types = ['a slice of pizza', 'two tacos', 'some sushi', 'a burrito'];
  return types[Math.floor(Math.random() * types.length)];
}
Enter fullscreen mode Exit fullscreen mode

And now our new method as part of our chained method code.

let sarah = new Person('Sarah');
sarah.speak('Hello').drink('tea').eat(food).speak('That was great!');
Enter fullscreen mode Exit fullscreen mode

We run this code and we get something like this:

Sarah says, Hello
Sarah is enjoying a tea
Sarah is eating two tacos
Sarah says, That was great!
Enter fullscreen mode Exit fullscreen mode

Start flexing πŸ’ͺ!

Common Array Method examples

Let's move back to that original example with the Array methods - filter and map.

let names = ['Tony', 'Adesh', 'Robert', 'Su Cheng'];
names = names.filter(someFunc).map(otherFunc);
Enter fullscreen mode Exit fullscreen mode

The Array.prototype.filter method will take a function as its parameter and run that function once on each item in the array. The filter method's function must return true or false for each item. true means keep the item. false means dispose of the item. After calling the function once per item, the filter method will return a new Array built based on those true and false responses from the function calls.

This new Array will be used to call the map method.

The Array.prototype.map method will take a function as its parameter and run that function once on each item in the array. The map method's function can return anything it wants. The map method builds a new Array containing all those returned values. The new Array will always have the same number of items as the Array that called the map method.

The new Array returned from the map method, since there is nothing chained onto it, will be assigned to the variable names.

Now you know how to chain your methods and why you want to.

Good for you! πŸ†πŸ₯‡πŸ’―πŸ™Œ

If you want to learn more about Arrays, Functions, Javascript or practically any web development topic: please check out my YouTube channel for hundreds of video tutorials.

Top comments (1)

Collapse
 
prof3ssorst3v3 profile image
Steve Griffith

Definitely. That's why you should use it as part of your best practices of creating pure functions and following good naming conventions.