Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript: Function Chaining

solution: nope. you can't do it. chain commands with && and be done with it.

tl;dr: in random.function("blah").check(), how do I make it ignore check() if function("blah") returns false? Or is this even possible?

The background:

Okay, so I'm horrid at code. But I'm trying to procrastinate by coding things that would make my life marginally easier. And my marginally, I mean microscopically. So basically, my entire project is based around objects and plugins. (plugins being additional .js files that contain an object. for example, afk.js looks something like this:

global.afk = function(){};
afk.check = function(){ ... };

etc, etc. And by loading that js file, I get access to the afk object and all the function therein. Probably not efficient, but it works and it makes figuring out what functions are doing what easier.

Back to the procrastination bit. Because I don't know what plugins are loaded, in some of the shared core .js files, there are these conditional statements: if (plugin.has("afk")) { afk.check(); }; However, I got bored and made plugin.has() return the actual object instead of 'true', so I could do:

plugin.has("afk").check();

which works as expected. Unless there is no afk plugin, which throws the obvious error of "false has no method check".

So that's that. Any ideas? Also, if you have any qualms with anything, let me know. I know I do a lot of things wrong/inefficiently, and it's nice to fix them when I figure it out.

like image 811
Dalton Gore Avatar asked Apr 15 '13 17:04

Dalton Gore


4 Answers

The returned object should have that function, otherwise you get an error. What you can do is make a base Object type, which is always returned. Than when you return it, it has an empty function and will not create an error.

Something like:

var baseObj = function(){
    this.afk = function(){
        return this;
    }
    this.check = function(){
        return false;
    }
};

var advObj = new baseObj();
advObj.afk = function(){
    // extended func
    return new baseObj();
}

var a = new advObj();
a.afk("test").check();
like image 58
Niels Avatar answered Oct 15 '22 22:10

Niels


So jQuery does it a bit like this:

$(plugin) -> returns a collection like object
.has("afk") -> filters the internal collection, and returns the collection
.check() -> action executed on this collection

So let's make a small example:

function Wrapper(ele) {
  // make our collection
  this.coll = [ ele ];

  // function to filter
  this.has = function(filter) {
    this.coll = this.coll.filter(function(e) {
      return e === filter;
    });

    // we return ourself for chaining
    return this;
  };

  // function to execute on ourself
  this.check = function() {
    // returns actual value
    return this.coll.length;
  };
}

var $ = function(ele) {
  // wrapper around the `new` syntax
  return new Wrapper(ele);
};

console.log($('jan').has('jan').check()); // gives 1
console.log($('pietje').has('jan').check()); // gives 0

Cool thing is you can now extend your Wrapper object from external files by extending the prototype:

Wrapper.prototype.hasNot = function(filter) { 
    /* do magic */
    return this;
}
like image 34
Jan Jongboom Avatar answered Oct 15 '22 22:10

Jan Jongboom


A short and dirty way to make this work would be to use the && operator.

var x = {};

x.afk && x.afk();

x.afk = function () { console.log("I exist!"); };

x.afk && x.afk();

Not your optimal wide range solution though.

like image 37
colin-higgins Avatar answered Oct 15 '22 22:10

colin-higgins


Within current ECMAScript specifications, turning .check() into a no-op (and any other method that might exist on the non-existent plugin) is impossible since you cannot as you've found portably access undefined properties of undefined objects.

The proposed "Direct Proxies" ECMAScript API would appear to solve this, but it's not standardised yet. I also suspect that it's not very efficient to proxy your code this way since you would (AIUI) have to make your .has() function return a trapping proxy object which then has to do real work for every "missed" function call. It would be far better IMHO to simply ensure that the conditional code is never run at all.

So, other than the && method proposed elsewhere, all I can offer is this:

plugin.if("afk", function() {
    // put conditional code here
});
like image 36
Alnitak Avatar answered Oct 15 '22 20:10

Alnitak