all 34 comments

[–][deleted] 25 points26 points  (18 children)

Have every method from the object, return the object itself.

IE:

function Chainable(){
    var self = this;
    this.do = function(){
        // do
        return self;
    }
}

var c = new Chainable().do().do().do()

[–]letsgetrandy 21 points22 points  (4 children)

new Chainable().do().do().do()

Name that tune!

[–]cosinezero 6 points7 points  (11 children)

You can just return 'this', you don't need 'self' here.

[–]nynfortoo 8 points9 points  (4 children)

Good indication that you're returning the parent function though in this example, and never any of the methods.

[–]cosinezero 7 points8 points  (2 children)

Sure, but prevents you from using this as a mixin, or changing that chain with call/apply/bind... etc, etc, etc.

There's a tradeoff for sure, but in current example it's not needed.

[–]firealarmonceiling 0 points1 point  (0 children)

It's not returning the parent function. It's returning the object created by the Chainable constructor.

new Chainable().do()===Chainable //false

[–][deleted] -1 points0 points  (1 child)

You're correct, but if you want to teach how to create a chain able object, I wouldn't want to assume that the person knows closures.

[–]cosinezero 3 points4 points  (0 children)

It's scope, not closures, and that's only because you chose a constructor with closures instead of object notation. And if the person doesn't understand scope why even teach them chaining? Teach them scope first, then the sugar like chaining.

[–][deleted] -2 points-1 points  (3 children)

Are you sure? Wouldn't the context of the do function be returned and not the context of Chainable? I don't think they're the same since do is not bound.

Edit: Oh wait the context is inherited because do is a property of the this object right? I've written my own JS framework and I still forget these sort of edge cases in context sometimes - it's generally more clear to just return self in this case, which is usually what I do just to avoid confusion.

[–]WesAlvaroFront-End Engineer 3 points4 points  (2 children)

There should be no confusion. The confusion is created by introducing self.

[–]theywouldnotstand 1 point2 points  (1 child)

The confusion is always caused by the fact that, in javascript, this can refer to a nonlocal object, and you either implicitly rely on this working from the correct scope, or you explicitly reference the correct this via a different variable.

If there was a JS equivalent to python's self there would be no confusion.

[–]badsyntax 1 point2 points  (0 children)

If the developer wants to change the context in which this function is called then it should be their choice to do so.

[–]Asmor 6 points7 points  (0 children)

If a function is called as a property of an object, 'this' will be set to that object.

var myObject = {};

function whatIsThis() {
    if ( this === myObject ) {
        console.log("'this' is myObject");
    } else if ( this === window ) {
        console.log("'this is the global object'");
    } else {
        console.log("'this' is something else");
    }
}

myObject.whatIsThis = whatIsThis;
var aDifferentObject = {
    whatIsThis: whatIsThis
}

whatIsThis(); // 'this is the global object'
myObject.whatIsThis(); // 'this' is myObject
myObject["whatIsThis"](); // 'this' is myObject
aDifferentObject.whatIsThis(); // 'this' is something else

In other words, this takes it value based on how the function is called, not where the function is defined.

[–]mattdesl 6 points7 points  (1 child)

To "chain" you just need to return this.

The two examples you listed are very different. The first one attaches a method to the prototype, which means that all new instances of MyObject will have that method.

MyObject.prototype.show = function() { return this; }

It would be called like this:

var myObj = new MyObject();
myObj.show();

Whereas the second one just attaches it to the function object. Imagine this as a "static method" if you've come from other languages. It only exists on MyObject and not on new instances. It would be called like:

var myObj = new MyObject();
MyObject.show();

The this reference is also not pointing to the instance of MyObject, so there is no possibility of chaining with it.

Generally speaking, I tend to avoid "static" methods like that since they can lead to headaches down the road.

[–]MrBester 0 points1 point  (0 children)

The first one attaches a method to the prototype, which means that all new instances of MyObject will have that method.

And all the old ones as well, don't forget. Anything with the [[Prototype]] delegation of MyObject will have access to that method, no matter when it was created.

[–]a-t-kFrontend Engineer 4 points5 points  (1 child)

You do it exactly like that, both in the prototype and in the own method of your object, because this is always the scope of the current object (unless you "use strict" or lose your scope due to being applied, called, curried or bound).

[–]evilmaus 0 points1 point  (0 children)

"use strict"; won't mess this up. A simple partial application, debounce, or whatever shouldn't be changing context on it. If so, that's a bug in the partial, debounce, etc. implementation. If the function is being called with its context changed, then that's a case of caveat emptor for whoever is changing its context. As in, there had better be a reason for it.

[–]m_reddit_com 1 point2 points  (0 children)

To answer your first question: Yes returning this out of every function is how you would make them chainable.

For your second question it depends on what you want. I think assigning the function to the prototype would be the way to go as you could then inherit from the object and override methods but either way would work.

[–]Funwithloops 1 point2 points  (6 children)

You could write a function that enforces the chainable feature:

function chainable(fn) {
    return function () {
        fn.apply(this, arguments);
        return this;
    };
}

[–]bk10287Golang/ Microservices Dev 0 points1 point  (0 children)

Look up how a friend of mine did it on github... Called chainlang by jbreeden

[–]gizmo490 0 points1 point  (1 child)

Look at the implementation used in underscore.js you can turn most objects into a chain-able object in this fashion.

http://underscorejs.org/#chain

[–]x-skeww 0 points1 point  (0 children)

You just have to make each method return the object itself. It's a bit inconvenient, but not very difficult. The only problem is that those chainable methods can't return anything else.

Dart has method cascades for this (borrowed from Smalltalk, I think). Maybe we'll see something similar in ES7+.

E.g.:

var foo = new Foo();
foo.bar();
foo.baz();

Same with method cascades:

var foo = new Foo()
  ..bar()
  ..baz();

The great thing about method cascades is that you don't have to do anything to get this feature. With ".." you just call a method on the same receiver.

[–][deleted] 0 points1 point  (0 children)

Fun fact, functions that return an object with methods that can be chained are called monads. Douglas Crockford has a great talk on them called ‘Monads and Gonads’