Josh Beam
Josh Beam

Reputation: 19772

How to chain methods after already returning a value?

The following does work:

//Returns an object with an "id" of 1, which is inside another object, "collection".
//Then, calls .move(), which sets collection's property "coordinates" to {x: 20, y: 20}

collection.get(1).move(20,20);

console.log(collection.get(1).get('coordinates')); //returns { x: 20, y: 20 }

This works because the collection .get() method executes return this;
Therefore, that method is chainable.

However, this does not work:

collection.get(1) //returns the object inside "collection" that has an "id" of 1
.get('coordinates') //returns that object's current "coordinates" { x: 20, y: 20 }
.move(20,20); //error

This is because the "object with id: 1" .get() method looks similar to this:

Thing.prototype = {
    id: null,
    get: function(prop) {
            if (prop == 'id') {
                return this.id; 
            } else {
                return this.attributes[prop];
            }
         } 
    }       

(The id is set when an object Thing is initialized, by the way.) As you can see, this individual .get() method already returns a value (a certain property of its object).
Therefore, given the way the code is set up now, the method isn't chainable.

To kind of wrap it all up a little bit, my "collections" hold an array of "things". Each collection has a .get() method, which returns a thing from the collection's array of things. In addition, each thing has a .get() method, which returns a value from a specific property in that thing.

The issue I'm having is that if possible, I'd like to make the indiviual thing .get() method chainable... but, I can't call both of these lines of code in the same block:

return this[prop];
return this;

I've looked at, for example, a code excerpt from one of John Resig's slides:

Function.prototype.bind = function(){ 
  var fn = this, args = Array.prototype.slice.call(arguments), object = args.shift(); 
  return function(){ 
    return fn.apply(object, 
      args.concat(Array.prototype.slice.call(arguments))); 
  }; 
};

which shows an example of returning a couple different things through the use of callback functions (I think).

I've tried several different ways of going about this, which didn't work, including returning an object of another method with a callback that returns the object.

Any pushes in the right direction would be appreciated.

Upvotes: 0

Views: 3277

Answers (3)

Ja͢ck
Ja͢ck

Reputation: 173572

The bottom line is that your function must return the object itself if you wish to keep the chain going; this is one way to do that:

get: function(prop, callback) {
        if (prop == 'id') {
            callback(this.id);
        } else {
            callback(this.attributes[prop]);
        }
        return this;
     }

This makes it chainable again because the side effect of the data fetch is now isolated, but as a result the logic may look a bit funny:

collection.get(1)
  .get('coordinates', function(value) {
     // whatever
  })
  .move(20,20);

Upvotes: 0

icktoofay
icktoofay

Reputation: 129001

If you only care about performing operations on sub-objects, you could look to jQuery's addBack and end for inspiration. That said, I'm not sure it's very good API design; the usage is much more clear if you just put an intermediate result in a variable and use multiple statements, even if that's not so ‘slick’.

Upvotes: 0

Barmar
Barmar

Reputation: 781068

A function can only return one thing. If it's purpose is to return data about the object, it can't also return the object itself. You can only chain methods that are used for their side effects or to return related collections.

Upvotes: 2

Related Questions