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);
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();
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);
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"
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}`);
}
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');
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;
}
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');
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?
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;
}
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)];
}
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!');
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!
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);
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)
Definitely. That's why you should use it as part of your best practices of creating pure functions and following good naming conventions.