How to make chainable function in JavaScript?
Asked Answered
U

3

26

Lets imagine function like this:

function foo(x) {
    x += '+';
    return x;
}

Usage of it would be like:

var x, y;
x = 'Notepad';
y = foo(x);
console.log(y); // Prints 'Notepad+'.

I'm looking for a way to create function that's chainable with other functions.

Imagine usage:

var x, y;
x = 'Notepad';
y = x.foo().foo().toUpperCase(); // Prints 'NOTEPAD++'.
console.log(y);

How would I do this?

Uniseptate answered 11/10, 2011 at 17:56 Comment(0)
B
27

Sure, the trick is to return the object once you're done modifying it:

String.prototype.foo = function() {
    return this + "+";
}

var str = "Notepad";
console.log(str.foo().foo().toUpperCase());

http://jsfiddle.net/Xeon06/vyFek/

To make the method available on String, I'm modifying it's prototype. Be careful not to do this on Object though, as it can cause problems when enumerating over their properties.

Buerger answered 11/10, 2011 at 18:0 Comment(11)
it's a good idea to at least check for a property on the native types before adding it, i.e. if ( !('foo' in String.prototype) ) {String.prototype.foo = function() {...} }Lapp
If you want to extend an object without breaking the enumeration, use the (semi-modern) Object.defineProperty: Object.defineProperty( String.prototype, {value:function(){ return this+"+"; } } ). By default the enumerable flag is set to false.Paralyze
@Lapp yes it is :). I assume the OP was only asking about strings as an example, so I kept the warnings to a minimum, but that's a good point.Buerger
Good solution, mine was too genericLeasia
Hmm... I have line like this = this.replace(diacritics[i].letters, diacritics[i].base);. It gives me 'Invalid assignment left-hand side.' in NetBeans.Uniseptate
This is how my code looks. Error is thrown on line #16.Uniseptate
You can't assign this in a string. Return the result instead. If you need to do multiple operations, store it in a temporary variable and return that instead. For example, var str = this; str += "foo"; return str;Buerger
Isn't it possible to create a chainable function attached to an object instead of to an String?Machado
@Machado Yes. A string is an object.Buerger
@AlexTurpin I mean, an element taken from the DOM using document.querySelectorMachado
@Machado should be. I would create a separate SO question with your problemBuerger
L
11

If I remember correctly, you can use "this" as a context of a function (object it belongs to) and return it to make the function chainable. In other words:

var obj = 
{
    f1: function() { ...do something...; return this;},
    f2: function() { ...do something...; return this;}
}

then you can chain the calls like obj.f1().f2()

Keep in mind, you won't be able to achieve what you are expecting by calling obj.f1().toUpperCase() - it will execute f1(), return "this" and will try to call obj.toUpperCase().

Leasia answered 11/10, 2011 at 18:0 Comment(1)
What would be the best practice to add .toUpperCase function?No
D
1

Here's a way to do it without messing with String.prototype, by returning an object similar to a string, with an additional method foo(). However, there are some downsides to this approach related to it not returning an actual string.

// Returns an object similar to a string, with an additional method foo()
function foo(str) {
  return Object.assign(`${str ?? this}+`, {
    foo
  });
}

var str = "Notepad";
console.log(
  "EXAMPLE - foo(str).foo().toUpperCase():",  
  foo(str).foo().toUpperCase()
);

console.log("---");

console.log("Some issues with this solution:");
console.log("typeof foo(str):", typeof foo(str));
console.log("foo(str).foo():", foo(str).foo());

console.log(
  "You may need to use toString() - foo(str).foo().toString():",
  foo(str).foo().toString()
);
.as-console-wrapper { min-height: 100% }
Dibranchiate answered 16/6, 2021 at 19:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.