Method chaining is a mechanism of calling a method on another method of the same object. There are different reasons for method chaining for different people. One of the major reasons for chaining methods is to ensure a cleaner and more readable code. Let's consider an example below:
var recipeObj = new Recipe();
recipeObj.addIngredient('salt');
recipeObj.addIngredient('pepper');
recipeObj.getIngredients()
With method chaining, the code snippet above can be refactored to:
var recipeObj = new Recipe();
recipeObj
.addIngredient('salt')
.addIngredient('pepper')
.getIngredients();
Looking at both snippets above, one would agree that the second is cleaner than the first.
What makes method chaining possible?
What makes the method chaining possible is the this
keyword. In JavaScript, the this
keyword refers to the current object in which it is called. Hence, when a method returns this
, it simply returns an instance of the object in which it is returned. Since the returned value is an object instance, it is, therefore, possible for other methods of the object to be called on the returned value which is its instance.
Method Chaining Example in ES5
function Recipe() {
this.ingredients = [];
}
Recipe.prototype.addIngredient = function (ingredient) {
this.ingredients.push(ingredient);
return this; // this makes chaining possible
}
Recipe.prototype.getIngredients = function () {
if (!this.ingredients.length) {
console.log('There is no ingredient in this recipe');
} else {
console.log(this.ingredients.toString());
}
}
var recipeObj = new Recipe();
recipeObj
.addIngredient('salt')
.addIngredient('pepper')
.addIngredient('maggi')
.getIngredients()
//salt,pepper,maggi
Method Chaining Example in ES6
class RecipeClass {
constructor() {
this.ingredients = [];
}
addIngredient = (ingredient) => {
this.ingredients.push(ingredient);
return this;
}
getIngredients = () => {
if (!this.ingredients.length) {
console.log('There is no ingredient in this recipe');
} else {
console.log(this.ingredients.toString());
}
}
}
const recipeObj2 = new RecipeClass();
recipeObj2
.addIngredient('garlic')
.addIngredient('pepper')
.addIngredient('maggi')
.getIngredients()
//garlic,pepper,maggi
Conclusion
In this short article, I explained the concept of method chaining in Javascript and demonstrated how it can be achieved using the this
keyword. You can give it a try. ✌️
Top comments (7)
If we are speaking javascript then the only thing that matters is the "shape" of the object that you return from the function.
For example, you can't chain anything to
getIngredients
because it returnsundefined
.Here is a fun thing you can do. Recently I learned about a data type that is called
Maybe
. One way of creating it is this.This gives you the behavior
array.map
but for regular objects. So you could make your functions.And now your chain can look like this.
See it in codepen
I remember when I tried to solve this chaining thing some years ago. It was full of mistery. I think this is the reason why beginners shout start with the basics things aka reference and primitive types, contexts, closure etc... Thanks for the clear article. It can be useful for a lot of people.
You're perfectly right Krisztian, some concepts are better and more naturally understandable with time. At some point, I used to battle with understanding chaining both in PHP but now, understanding it is just seamless. Thank you.
How can we decompose this into multiple classes? @nedsoft
I think it depends on what you want to achieve, you could split it to multiple classes and have them inherit each other depending on what you want to achieve
I have a Validator class which is nothing but the base class having methods like validate(), and some root properties which will be needed for processing. Then we will have different classes like Type Validator which will have methods validate types like isString, isInt, etc. Then we will have classes like DB Validator which will have methods like isDuplicateUser, etc.
In order to use this Validator I will import and call chained methods like
Validator().isString().isDuplicate().validate("username")
@nedsoftVery useful!