0

I'm curious if this is possible in JavaScript.

I want a factory function that can return functions whose actual contents are different. Say this is our factory:

function makeFunction(a, b, c) {
  // Implement me
  return f;
}

I want it to work like this:

> makeFunction(true, true, false);
=> function() { do_a(); do_b(); }
> makeFunction(false, true, false);
=> function() { do_b(); }
> makeFunction(true, false, true);
=> function() { do_a(); do_c(); }

Setting aside whether or not this is a good idea ... Can I do this? If so, what needs to happen inside makeFunction?

Bonus: Generally, what is this pattern called?


Edit: I should clarify something I'm not looking for: the sane approach that uses closures. E.g. the following has the correct effect, but doesn't change the function contents/body:

function makeFunction(a, b, c) {
  return function() {
    if (a) { do_a(); }
    if (b) { do_b(); }
    if (c) { do_c(); }
  }
}
dee-see
  • 23,668
  • 5
  • 58
  • 91
GladstoneKeep
  • 3,832
  • 2
  • 26
  • 38
  • Factory pattern and it's generating Closures, look them up, you'll find plenty of resources – ars265 Aug 27 '13 at 22:39
  • You should say why you need this, right now it just looks like really poor design or a really strange requirement. Closures is the way to have functions return other functions where the behavior is different because of what you passed in. – Ruan Mendes Aug 27 '13 at 22:44
  • It is poor design and I would not use this in real-world code. But I think it's an interesting question. – GladstoneKeep Aug 27 '13 at 22:47
  • It's not, if there's no reason for it... -1 until you make the question less open ended. A questions that asks about pros and cons is not a good fit for SO. Try http://programmers.stackexchange.com/ – Ruan Mendes Aug 27 '13 at 22:48
  • My question is clearly stated and posted with good intent, so could you remove the downvote? There are plenty of other questions to look at if you happen to find mine uninteresting. Sheer curiosity is more than enough reason to ask a question. – GladstoneKeep Aug 27 '13 at 22:56

5 Answers5

1

This solution takes care of it all for you, define the functions you want in the func array using their names and then use it to create.

function a (){
    console.log("a");
};
function b() {
    console.log("b");
};
function c() {
    console.log("c");
};
function makeFunction() {
    var funcs = [a, b, c];
    var finalfunc = [];
    for (var i = 0; i < arguments.length && i < funcs.length; i++) {
        if (arguments[i]) {
            finalfunc.push(funcs[i]);
        }
    }
    return function () {
        var f = finalfunc;
        for (var a = 0; a < f.length; a++) {
            f[a]();
        }
    };
};

Examples:

var z = makeFunction(true);
z();//outputs a

var z = makeFunction(true, false, true);
z();//outputs a c

var z = makeFunction(true, true, false);
z();//outputs a b

var z = makeFunction(true, true, true);
z();//outputs a b c

var z = makeFunction(true, true, true, true);
z();//outputs a b c

EDIT

I didn't see until I had wrote it that you didn't want closures, but I will keep this here anyway

Although you have said this, this is probably a safer way. It also directly answers the question excluding that fact, I have created a function with only the functions in it that you want to be run inside of it. There is no if statement for every function, it simply depends on the functions you have provided and the arguments :)

It is also completely dynamic and doesn't rely on defining conditions for each function.

FabianCook
  • 20,269
  • 16
  • 67
  • 115
1

Yes, this is possible. No, I'm not going to comment on the (perceived) value of such an approach.

Here's a quick sample:

window.addEventListener('load', mInit, false);

function createFunction(funcName, message)
{
    var script = document.createElement('script');
    var scriptContents = 'function ' + funcName + '()\n';
    scriptContents += '{\n\talert("' + message + '");\n}\n';
    script.appendChild( newTxt(scriptContents) );
    document.head.appendChild(script);
}

function mInit()
{
    createFunction('myFunc', 'hello world');
    myFunc();
}

Output:

<script>function myFunc()
{
    alert("hello world");
}
</script>

I wasn't sure the function would work immediately, hence the test as shown above. (it does, btw)

I had an idea in the past that involved hiding a script in the alpha channel of images...

enhzflep
  • 12,927
  • 2
  • 32
  • 51
  • Thanks for answering -- I would never have thought of this! Makes me wonder what else might be possible with a similar technique. – GladstoneKeep Aug 27 '13 at 23:09
  • A pleasure. Thanks for asking! While the function myFunc will still work, you can add code to the function that will remove the added script tag from the DOM. Just give the script an id (say, 'addedScript'), then insert this as the last line of the injected script: `document.head.removeChild( document.getElementById("addedScript") );` As I say, it will still work, but you can no longer see it in the Element Inspector (Chrome 29) You could label scripts (read: give them an id) then delete them afterwards, to hide any juicy code. ;) – enhzflep Aug 27 '13 at 23:20
  • 1
    Just wanted to share [my own answer below](http://stackoverflow.com/questions/18476591/factory-that-builds-functions-whose-contents-are-different-javascript/18491664#18491664) which I thought you would find interesting. Came across this technique after doing some more research. – GladstoneKeep Aug 28 '13 at 15:08
  • @mtoast - Thanks for the update! Bookmarked. I certainly do like that approach, it's infinitely more readable than mine. I'll have to try this out in the future. :thumbsup: – enhzflep Aug 28 '13 at 15:30
1

You can use the Function constructor to accomplish this. For example:

// Globals can be seen by the function we're going to create.
function do_a() { /* stuff */ }
function do_b() { /* stuff */ }
function do_c() { /* stuff */ }

function makeFunction(a, b, c) {
  var contents = [];
  if (a) { contents.push("do_a();"); }
  if (b) { contents.push("do_b();"); }
  if (c) { contents.push("do_c();"); }
  return new Function(contents.join(''));
}

console.log(makeFunction(true, true));
console.log(makeFunction(true, false, false));
console.log(makeFunction(false, true, true));

The above code will print the following to the console:

function anonymous() {
  do_a();do_b();
}
function anonymous() {
  do_a();
}
function anonymous() {
  do_b();do_c();
}

I believe this satisfies the requirements of the question. Some JavaScript libraries actually do use this technique to compile extremely efficient functions based on the needs of the program at runtime. However, there are a few notable downsides to this approach:

  • Functions created with the Function constructor only inherit the global scope. Depending on what your code needs to do, this could be a deal-breaker.
  • Function construction in this way is slow, because the engine must convert the function source to executable code. (Note: This doesn't mean the created functions themselves will be slow. In fact, they will run normally.)
  • It's hard to write functions like this. (Quoting/unquoting.)
  • The function contents wouldn't be lint-able.
GladstoneKeep
  • 3,832
  • 2
  • 26
  • 38
0

The following "manual" approach that would get the job done, but is only recommended if you only need to handle a few conditions and/or if you like typing.

function makeFunction(a, b, c) {
   if (a && b && c) {
      return function() { do_a(); do_b(); do_c(); }
   }

   if (a && b && !c) {
      return function() { do_a(); do_b(); }
   }

   // You get the idea...
}

Pros: It works? Cons: If you need to provide for a lot of permutations... good luck :)

GladstoneKeep
  • 3,832
  • 2
  • 26
  • 38
0

You can use an object and return it's methods.

I really don't see any point to this though.

function makeFunction(a, b) {
 var f = {
    a:function(){console.log("IcanBeANythign")},
    b:function(a,b){return a+b}
  };
  if(a){
  return f.a;
  }
  if(b){
  return f.b;
  }
}
var f = makeFunction(false,true)(3,4) // 7

Something similar is currying or more accurately partial application . It has been answered here quite thoroughly

You can do something like this:

function curryPlus(a){
          return function(b){
          return a+b;
          }
    }

var add1 = curryPlus(1);
add1(2) // Outputs: 3
Community
  • 1
  • 1
raam86
  • 6,785
  • 2
  • 31
  • 46