1

I'm trying to get the example code below to work.
Edit (Trying to be more clear what the aim is):

I wan't to make all the functions and variables of the obj available in the functions setup and draw as they where globals. This is not for the browser. This is for Adobe ExtendScript so I can only use EcmaScript 3 and some polyfills. The lib.js file is provided by me and gets included before the the user-script.js file. The user has a reference what functions are availble and might use them within setup and draw. This is actually pretty similar to what P5.js does but I'm are trying to achieve this for InDesign. We can of course call the obj.foo(). The aim is though to get rid of the obj. to give the user the possibility to just call foo and get the result of obj.foo.

This is lib.js. It is just a small part of the library to illustrate what I have at hand.

var obj = {
  foo: function() {
    console.log('foo');
  },
  bah: function() {
    console.log('bah');
  },
  foobah:function(arg){
    return arg*2;
  },
  CONST:"Hello World",
  go:function(){
    // looks in the global scope if there is
    // a setup and draw function and should patch them.
    if(typeof glob.setup === 'function'){
      glob.setup();
    },
    if(typeof glob.draw === 'function'){
      glob.draw();
    }
  }
};

This could be the user-script.js. The structure we Provide is:

  • #include for the lib
  • function setup(){}
  • function draw(){}
  • obj.go() as execution of everything (might be removed later on)

I can not tell the user to write more additional code into setup and draw. The part for the user should be so reduced he can write this by hand and does not need to use a boilerplate or something like this.

#include lib.js

function setup() {
  foo(); // might be called or not
  console.log('in setup');
}

function draw() {
  bah();// might be called or not
  console.log('in draw');
}
obj.go()

Thanks to all your answers. I will review them and get back to report what was the final decision. All of them seem to solve the problem in different ways. I currently can't tell which is the "right" answer. Those we seemed be nearest to my question got an upvote from me.

fabianmoronzirfas
  • 4,091
  • 4
  • 24
  • 41
  • 3
    You cannot add functions to other functions, but you can compose two functions to produce a new one – elclanrs Jul 01 '16 at 13:49
  • Do you have any examples for that? – fabianmoronzirfas Jul 01 '16 at 13:50
  • 1
    `function compose(f, g){return function(){return f(g.apply(this, arguments))}}; var h = compose(f, g)` – elclanrs Jul 01 '16 at 13:51
  • What does prevent you from just calling obj.foo() and obj.bah() in fn1() / fn2()? – Arnauld Jul 01 '16 at 13:52
  • 1
    why not to call fn1(obj.foo); while implementation of fn1 is like function fn1(foo){} – binariedMe Jul 01 '16 at 13:53
  • 1
    To expand, your functions are not really functions persay, they don't take any arguments or return anything, they are more like procedures for side-effects, so you may want to look into something like `function after(f, g){return function(){f(); g()}}` or `before` where you'd execute in the opposite order. – elclanrs Jul 01 '16 at 13:54
  • @Arnauld the part in main is written by somebody else. I want to "patch" these functions with additional functions. For easier usage I want to get rid of the `obj.` – fabianmoronzirfas Jul 01 '16 at 13:55
  • So, `fn1` is hardcoded to call `foo`, and you want to change which `foo` exactly it calls? Otherwise this makes little sense. – deceze Jul 01 '16 at 13:56
  • @elclanrs Well it could be that obj.foo returns something or takes an argument. I just wanted to boil this down. I actually don't get your code in the comment. What is `f` in my case what is `g`? – fabianmoronzirfas Jul 01 '16 at 13:57
  • `f` and `g` are just functions. I agree with deceze in that it makes no sense to replace a function that has already been defined, you cannot do this programmatically, but you can produce a new function that's a combination of other functions. So depending on what you need you can use `compose`, `after`, `before` and so on – elclanrs Jul 01 '16 at 13:59
  • @deceze @elclanrs This is for a larger project that aims to do something similar to P5.js There the user writes the functions `setup` and `draw` and and can use existing functions like `random(100,100)` and constants like `PI` in them without having them declared. – fabianmoronzirfas Jul 01 '16 at 14:04
  • The magic how to do this is somewhere in here https://github.com/processing/p5.js/blob/master/src/core/core.js but I can't wrap my head around it how to do that. – fabianmoronzirfas Jul 01 '16 at 14:08
  • posted an answer below. I think `Function.prototype`s might be a simple way to do this. – trk Jul 01 '16 at 15:06
  • "*as if they where globals*" - what prevents you from actually making them global? It sounds like that is what you want. – Bergi Jul 04 '16 at 17:56
  • 1
    It sounds like you are looking for `with (obj) { … }`, but that of course requires cooperation of the user. – Bergi Jul 04 '16 at 17:58
  • We wont have the cooperation of the user. The target users are designers with no programming experience so may the global it is. – fabianmoronzirfas Jul 05 '16 at 05:52

11 Answers11

1

This does as you ask using closures and a higher order function, but obj is in scope of fn1 and fn2 so the actual function doesn't need to be injected you could just call obj.foo from within each function.

var obj = {
  foo: function() {
    console.log('foo');
  },
  bah: function() {
    console.log('bah');
  }
};

function higherOrderF(f,name){
  return function(){
    name && console.log(name)
    return f.apply(null,arguments)
  }
}

var fn1 = higherOrderF(obj.foo, 'fn1')
var fn2 = higherOrderF(obj.bah, 'fn2')

fn1();
fn2();
James Wakefield
  • 526
  • 3
  • 11
1

I think prototypes are a way to go. So, I am using Function.prototype in my solution. Please check if this works for you:

var obj = {
    foo: function() {
        console.log('food');
    },
    goo: function() {
        console.log('good');
    }
}

Function.prototype.add = function(fun) {
    if (!this.list) {
        this.list = [];
    }
    this.list.push(fun);
}

function a() {
    if (a.list) {
        a.list.forEach(function(f) {
            f();
        })
    }
    console.log('done at a');
}

function b() {
    if (b.list) {
        b.list.forEach(function(f) {
            f();
        })
    }
    console.log('done at b');
}

function main() {
    a.add(obj.foo);
    b.add(obj.goo);
    a();
    b();
    console.log('done at main');
}

main();

I have tried to use as much of how you would call (in terms of structure).

Do this solve your problem ?

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
trk
  • 2,106
  • 14
  • 20
  • Hm. Not really. I can't have content in a and b. These are up to the user to write. Having a basic structure in there confuses them. – fabianmoronzirfas Jul 01 '16 at 15:17
  • I see where you are you going. You want some kind of callback when `function a` is being called. – trk Jul 01 '16 at 15:23
  • I have a question however: how come you have `foo()` called within fn1. Is that allowed. Would you then have `foo()` and `obj.foo()` and `console.log('fn1')` when you call `fn1()` ? – trk Jul 01 '16 at 15:25
  • @fabiantheblind do you control `main()` and calls to user written methods `a()` and `b()` ? – trk Jul 01 '16 at 15:31
  • Yes I do have the control – fabianmoronzirfas Jul 01 '16 at 15:36
  • @Peter and 82Tuskers it works but when I use bah() or foo() in fn1 or fn2 I get a double output. Looks like they are executed on call of fn1. I just want to make them available. They might be called but must nut – fabianmoronzirfas Jul 01 '16 at 16:01
  • @fabiantheblind, ok, this is a new scenario. Do I understand you right that you are going to leave the decision of whether to call `foo` derived from `obj.foo` or `bah` / `obj.bah` up to the logic implemented within `fn1` respectively `fn2`? – Peter Seliger Jul 01 '16 at 16:09
  • Yes That's right. I wrote the question when I left work. Now I'm at home and need to review all those great answers. Thanks for all your effort. I also need to rewrite the question. – fabianmoronzirfas Jul 01 '16 at 16:11
  • @PeterSeliger Why did you prepend the same intro as in your own answer to this? – Bergi Jul 04 '16 at 18:01
  • @Bergi - I somehow accidentally did edit the wrong textfield/section of an answer that was not mine. I immediately after did mention it in a comment to the reviewer asking for reverting my changes. But nothing did happen. The review process even did let pass my changes. I'm really sorry since my text now has completely replaced "82Tuskers" original answer. – Peter Seliger Jul 04 '16 at 18:09
  • @PeterSeliger I see. I've rolled back your edits and flagged the review for moderator attention. – Bergi Jul 04 '16 at 18:14
1

As it already has been mentioned, the only pattern that helps one achieving the goal of changing/altering control flow of closed functions/methods (Thus, one does not own theirs code bases.) is function-composition.

It got pointed too, that there are differently implemented aproaches to it.

The OP's altered example will make use of a prototypal implementation of Function.before. Because JavaScript already features a standardized bind, I'm firmly convinced that Function.prototype is the right place as well for some other method-modifiers like before, after, around, afterThrowing and afterFinally.

... sticking as close to the OP's example as possible:

var
    obj = {
        foo: function() {
            console.log('foo');
        },
        bah: function() {
            console.log('bah');
        }
    };

function fn1() {
    //foo();
    console.log('fn1');
}

function fn2() {
    //bah();
    console.log('fn2');
}

function main() {
    //fn1.add(obj.foo); // <= Should add function foo to fn1
    //fn2.add(obj.bah); // <= Should add function bah to fn2

    window.fn1 = window.fn1.before(obj.foo);
    window.fn2 = window.fn2.before(obj.bah);

    fn1(); // Should output: foo and then fn1
    fn2(); // should output: bah and then fn2

    //obj.foo = obj.foo.after(f1);
    //obj.bah = obj.bah.after(f2);
}

main();

... implementation of Function.prototype.before:

(function (Function) {
    var
        isFunction = function (type) {
            return (
                (typeof type == "function")
                && (typeof type.call == "function")
                && (typeof type.apply == "function")
            );
        },
        getSanitizedTarget = function (target) {
            //return (target == null) ? null : target;
            return ((target != null) && target) || null;
        };

    Function.prototype.before = function (handler, target) { // before
        target  = getSanitizedTarget(target);
        var proceed = this ;

        return (isFunction(handler) && isFunction(proceed) && function () {
            var args = arguments;

            //handler.apply(target, args);
            handler.call(target, args);
            return proceed.apply(target, args);

        }) || proceed;
    };
}(Function));

Taking into account the OP's answer ...

A: Yes That's right ...

... to this question ...

Q: ... Do I understand you right that you are going to leave the decision of whether to call foo derived from obj.foo or bah / obj.bah up to the logic implemented within fn1 respectively fn2?

... the former approach of mine changes from Function.before to Function.around.

However, personally I'm not pleased with this solution, for both globally provided methods fn1 and fn2 now need to anticipate the modification in advance, which in my eyes is not a clean approach.

Nevertheless, the next example comes close to what the OP is looking for:

var
    obj = {
        foo: function() {
            console.log('foo');
        },
        bah: function() {
            console.log('bah');
        }
    };

// - both next function's arguments-signatures
//   do already anticipate the modification.
// - this is considered to be a dirty approach,
//   please check your architecture or your
//   concept of control flow.

function fn1(foo, fn1, args) {

    foo(); // does not necessarily need to be called.

    console.log('fn1');
}
function fn2(bah, fn2, args) {

    bah(); // does not necessarily need to be called.

    console.log('fn2');
}

function main() {
    //fn1.add(obj.foo); // <= Should add function foo to fn1
    //fn2.add(obj.bah); // <= Should add function bah to fn2

    window.fn1 = obj.foo.around(window.fn1);
    window.fn2 = obj.bah.around(window.fn2);

    fn1(); // Should output: foo and then fn1
    fn2(); // should output: bah and then fn2
}

main();

... implementation of Function.prototype.around:

(function (Function) {
    var
        isFunction = function (type) {
            return (
                (typeof type == "function")
                && (typeof type.call == "function")
                && (typeof type.apply == "function")
            );
        },
        getSanitizedTarget = function (target) {
            //return (target == null) ? null : target;
            return ((target != null) && target) || null;
        };

    Function.prototype.around = function (handler, target) { // around
        target  = getSanitizedTarget(target);

        var proceed = this ;
        return (isFunction(handler) && isFunction(proceed) && function () {

            return handler.call(target, proceed, handler, arguments);

        }) || proceed;
    };
}(Function));
Peter Seliger
  • 11,747
  • 3
  • 28
  • 37
1

with is an easy solution

var obj = {
  foo: function() {
    console.log('foo');
  },
  bah: function() {
    console.log('bah');
  },
  proto: null
}

//the frowned upon with statement use with caution!!!
with(obj){
function fn1() {
  foo();
  console.log('fn1');
}

function fn2() {
  bah();
  console.log('fn2');
}

function main() {
  fn1(); // Should output: foo and then fn1
  fn2(); // should output: bah and hen fn2
}
}
main()

//Dependency Injection
//es6 can do this a lot cleaner.
;(function(o){
  var foo = o.foo,
      bah = o.bah;
  
  function fn1() {
    foo();
    console.log('fn1');
  }
  
  function fn2() {
    bah();
    console.log('fn2');
  }
  
  function main() {
    fn1(); // Should output: foo and then fn1
    fn2(); // should output: bah and hen fn2
  }
  main() 
}(obj))


//es6 Dependency Injection 0 globals
;(({foo,bah} = obj)=>{  
  function fn1() {
    foo();
    console.log('fn1');
  }
  
  function fn2() {
    bah();
    console.log('fn2');
  }
  
  function main() {
    fn1(); // Should output: foo and then fn1
    fn2(); // should output: bah and hen fn2
  }
  
  main()
})()
James Wakefield
  • 526
  • 3
  • 11
  • Yes the `with` approach works but it means I have to wrap the functions fn1 and fn2 in an additional scope. This is also something that might confuse the users. – fabianmoronzirfas Jul 01 '16 at 16:04
  • 1
    Then lets face it you want the functions to behave as though they are global, then just make them global in the first place. if they are in a separate script file just load them and they will be there available to your users. If you don't want them to be global then that means you have to do a little extra to make them work. – James Wakefield Jul 01 '16 at 16:13
  • The thing is making it global would mean a full rewrite of the library. Having them added to the scope of the functions is a smaller change – fabianmoronzirfas Jul 01 '16 at 16:37
  • 1
    @fabiantheblind: It's either `with`, globals, or explicit referencing with `obj.` (unless you consider [arcane magic](http://stackoverflow.com/a/24098008/1048572)) – Bergi Jul 04 '16 at 18:04
  • @Bergi Magig sounds good. :-) Maybe we have to go for the global scope. – fabianmoronzirfas Jul 05 '16 at 05:50
1

OP: I don't get the idea of the module system. ...

With a (custom) module system one was going to provide a method to ones users via that they were able to register theirs code bases, encapsulated each into a function itself. Since there was control of the "registering", it was up to oneself of which additional state/behavior should be injected into such function based modules. One just has to make clear which arguments would be passed into those modules by ones system.

OP: ... I will have a look into your before prototype. This looks like it could work for us. ...

It won't. But another more straightforward approach will do. It will misuse the idea of a module system and turn things inside out. The OP's requiremnts are far from being ideal. But a possible solution, refactored from the OP's provided latest example code, that - within the given scenario - does what the OP might look for, could look similar to that ...

lib.js

// lib.js

(function () {
    // do not unnecessarily pollute the global namespace.

    function foo() {
        console.log('foo');
    }
    function bar() {
        console.log('bar');
    }

    function biz() {
        console.log('biz');
    }
    function baz() {
        console.log('baz');
    }

    function foobar(arg){
        return (arg * 2);
    }

    function executeGlobalCustomCode() {

        // does look for the existence of both globally
        // scoped methods `setup` and `draw` and, if so,
        // calls them with the right arguments.

        if (typeof global.setup === 'function') {

            global.setup(foo, bar);
        }
        if (typeof global.draw === 'function') {

            global.draw(biz, baz);
        }
    }

    var
        global = (function () {
            return this;
        }).call(null),

        CONST = "Hello World";

    // do only expose into global namespace what is really needed.
    global.util = {
        go: executeGlobalCustomCode
    };

}());

custom code with "lib.js" included.

// custom code with "lib.js" included.

function setup(foo, bar) {

    console.log('within SETUP :: call "foo" and "bar".');

    foo(); // might be called or not
    bar(); //
}

function draw(biz, baz) {

  //console.log('within DRAW :: call "biz" and "baz".');
    console.log('within DRAW :: call "baz" only.');

  //biz(); // might be called or not
    baz(); //
}

util.go(); // does trigger execution of `setup` and `draw`.
Peter Seliger
  • 11,747
  • 3
  • 28
  • 37
  • The problem here is that we have a lot off functions that need to be present in draw and setup. So passing them one by one, is not an solution. We could pass them ass object but then we are back at square one. Thanks for all your thought Peter. I will rethink the idea and maybe we have to go for a global scope. – fabianmoronzirfas Jul 05 '16 at 05:55
  • @fabiantheblind - If you are looking over my most recent example, its implementation hopefully will convince you, thinking over all of yours architectural approach(es) for making yours and yours customer's code playing together in a more convenient way. http://stackoverflow.com/questions/38147118/add-function-into-the-scope-of-another-function-javascript/38205931#38205931 – Peter Seliger Jul 05 '16 at 14:37
  • luckily there is no "customer" it is for a project called Basil.js https://github.com/basiljs/basil.js which we use to teach programming at the university where I work – fabianmoronzirfas Jul 05 '16 at 15:06
1

OP: The problem here is that we have a lot of functions that need to be present in draw and setup. So passing them one by one, is not a solution. We could pass them as object but then we are back at square one. ... I will rethink the idea and maybe we have to go for a global scope.

Just out of curiosity and because I never ever was in need of the black magic of eval and with, I finally went for it, proving that Frankenstein's monster really can be created via those tools. There it is, this poor creature ...

... Victor ...

// lib.js

(function () {
    // do not unnecessarily pollute the global namespace.

    var toolkit = {

        foo : function foo() {
            console.log('foo');
        },
        bar : function bar() {
            console.log('bar');
        },

        biz : function biz() {
            console.log('biz');
        },
        baz : function baz() {
            console.log('baz');
        }
    };

    var globallyExecutablesNameList = [
        "setup",
        "draw"
    ];

    function badlyEvalPatchAndExecuteGlobalCustomCode() {

        // does look for the existence of listed globally
        // scoped methods and, if so, patches them with a
        // bad "eval" approach (using `new Function`) that
        // within this eval uses an `with (context)` which
        // is considered to be error prone.

        var
            fct,

            str,
            args,
            body,
            result,

            regXFctArgsAndBody  = (/function[^(]*\(([^)]*)\)\s*\{(.*)\}/);

        globallyExecutablesNameList.forEach(function (fctName) {
            fct = global[fctName];

            if (typeof fct === "function") {

                str     = fct.toString().replace((/\n/g), "###__NEW__LINE__###");
                result  = regXFctArgsAndBody.exec(str);

                if (result) {
                    body = [
                        "with (this) {",  // within new method's `this` context ...
                                          //    do stuff, that was derived from ...

                        result[2].split(/###__NEW__LINE__###/).join("\n"),

                                          // ... a (global) custom function's body.
                        "}"               //
                    ].join("");

                    args = result[1]
                        .replace((/###__NEW__LINE__###/g), "")
                        .replace((/\s+/g), "")
                        .split(",");

                    args.push(body);

                    fct = Function.apply(null, args); // frankenstein's monster.

                  //console.log("args : ", args);
                  //console.log("fct.toString() : ", fct.toString());
                }
                fct.call(toolkit); // provide `toolkit` as this function's context.
            }
        });
    }

    var
        global = (function () {
            return this;
        }).call(null);

    // do only expose into global namespace what is really needed.
    global.lib = {
        go: badlyEvalPatchAndExecuteGlobalCustomCode
    };

}());

... and it's victims ...

// custom code with "lib.js" included.

function setup(/*a, b, c*/) {

    console.log("SETUP");

    foo();
    bar();
}

function draw(/*x, y*/) {

    console.log("DRAW");

  //biz();
    baz();
}

lib.go(); // does look for `setup` + `draw` + processes them by 2 dirty techniques.
Peter Seliger
  • 11,747
  • 3
  • 28
  • 37
0

I see the problem of adding to global scope, here's a new way using eval

var obj = {
  foo: function() {
    console.log('foo');
  },
  bah: function() {
    console.log('bah');
  }
};

function fn1() {
  foo();
  console.log('fn1');
}

function fn2() {
  bah();
  console.log('fn2');
}

function main() {
  function myFunc(fn) {
    function foo() {
        obj.foo();
    }
    function bah() {
        obj.bah();
    }
    eval("this.exec = " + fn.toString());
  }

  var myNewFunc1 = new myFunc(fn1);
  myNewFunc1.exec(); //foo, fn1
  var myNewFunc2 = new myFunc(fn2);
  myNewFunc2.exec(); //bah, fn2
}

main();

Working fiddle

Arg0n
  • 8,283
  • 2
  • 21
  • 38
  • Thanks @Arg0n But I can't add additional code to the `fn1` and `fn2` functions. These are written by others. – fabianmoronzirfas Jul 01 '16 at 13:59
  • Yes this could work but I would love to not have them in global scope. :-) Thats why I have that object. – fabianmoronzirfas Jul 01 '16 at 14:07
  • does the function that you need called from within fn1 and fn2 actually change the response or is it just to cause a side effect? – James Wakefield Jul 01 '16 at 14:25
  • Hm. I actually can not. The library looks into global scope of fn1 exists and executes it. The idea is to provide a clean structure like Processing or P5.js to make it easier to write scripts. – fabianmoronzirfas Jul 01 '16 at 14:26
  • @JamesWakefield it can change everything. That's why I thought there is something that injects the functions of obj into the scope of fn1 – fabianmoronzirfas Jul 01 '16 at 14:28
  • the loop just adds foo to the global object as it is bound to `this` which is the global object, foo and bah are called directly from the functions, which in the first place doesn't make sense that they would call functions that don't exist. – James Wakefield Jul 01 '16 at 15:17
  • Yes I know it's wired to write functions that are not declared in the file. The aim is to provide a set of std functions the user can access without worrying where they are. Actually they get included by ExtendScript in top of the users script. Until now we used the obj.foo notation with obj in global scope. Now we want to get rid of the obj. like P5.js does – fabianmoronzirfas Jul 01 '16 at 15:21
  • @fabiantheblind Updated again, now using `eval`. – Arg0n Jul 01 '16 at 15:53
  • 1
    @Arg0n The version you had before worked also fine. I would rather not use eval. But thanks anyway. – fabianmoronzirfas Jul 01 '16 at 15:56
  • It worked, but added foo and bar to the global scope. This does not. – Arg0n Jul 01 '16 at 16:07
  • Okay. I like to evaluate both versions. Added yours without eval in an edit. Still needs to be reviewed – fabianmoronzirfas Jul 01 '16 at 16:09
  • Rejected, since it was a bad answer. You did not want foo and bar to be in global scope. – Arg0n Jul 01 '16 at 16:11
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/116219/discussion-between-fabiantheblind-and-arg0n). – fabianmoronzirfas Jul 01 '16 at 17:04
  • How to avoid eval in your answer and how to not have to write an wrapper around every function of the lib? – fabianmoronzirfas Jul 01 '16 at 17:07
  • You can write the functions directly in the wrapper, instead of in obj. I can't think of a way to achieve this without eval. Don't know how to change the closure of fn1 in another way. – Arg0n Jul 01 '16 at 19:36
  • @fabiantheblind - you mentioned ... >> ... Until now we used the obj.foo notation with obj in global scope. Now we want to get rid of the obj. like P5.js does ... << The only clean approach that achieves this task and I can think of, is providing kind of an own module system. The user then writes its own process logic within a function scope you are always in control of. Providing your predefined functions via this system does not pollute the global object either. – Peter Seliger Jul 02 '16 at 09:17
  • @PeterSeliger I don't get the idea of the module system. In our case the users writes his own script within `setup` and `draw` and manipulates an InDesign document. I will have a look into your before prototype. This looks like it could work for us. – fabianmoronzirfas Jul 02 '16 at 13:41
  • @fabiantheblind - http://stackoverflow.com/questions/38147118/add-function-into-the-scope-of-another-function-javascript/38189368#38189368 – Peter Seliger Jul 04 '16 at 16:56
0

You can pass a function to a function.

var obj = {
  foo: function() {
    console.log('foo');
  },
  bah: function() {
    console.log('bah');
  }
};

function fn1( passedFoo ) {
  passedFoo();
  console.log('fn1');
}

function fn2( passedBah ) {
  passedBah();
  console.log('fn2');
}

function main() {
  fn1(obj.foo); // Should output: foo and then fn1
  fn2(obj.bah); // should output: bah and hen fn2
}
Kristoffer K
  • 2,053
  • 18
  • 23
  • Thank you Kristoffer but there are more then these two functions in my object. The idea was to iterate over all keys in the object and dynamically add them. I just boiled it down to not have such a long code block. – fabianmoronzirfas Jul 01 '16 at 14:17
0

This is the monkey patched solution it is really replacing the original functions with new ones that still call the original function... so it works as though you had injected code into them. You could still use a higher order function to help compose these monkey patches.

var obj = {
  foo: function() {
    console.log('foo');
  },
  bah: function() {
    console.log('bah');
  }
};

function fn1() {
  //foo();
  console.log('fn1');
}

function fn2() {
  //bah();
  console.log('fn2');
}

main()
function main() {
  //monkey patched functions
  var originalFn1=fn1
  fn1 = function(){
     obj.foo()
     return originalFn1.apply(this,arguments)
  }
  
  var originalFn2=fn2
  fn2 = function(){
     obj.bah()
     return originalFn2.apply(this,arguments)
  }
  
  fn1(); // Should output: foo and then fn1
  fn2(); // should output: bah and hen fn2
}
James Wakefield
  • 526
  • 3
  • 11
  • The calls of the functions obj.foo are not hard coded. It could be the user uses them but it must not. Could be he uses something different. I don't know what happens in fn1 and fn2. I just want to make the functions foo and bah available in them without using the obj. Maybe I need to rewrite my question. – fabianmoronzirfas Jul 01 '16 at 14:51
  • that doesn't make any sense. If fn1 and fn2 are already written they aren't going to just suddenly start using random functions. you can pass parameters to a function, some functions take functions as parameters. – James Wakefield Jul 01 '16 at 15:03
  • I will try to rewrite my question asap. Maybe then it makes more sense to you. In some minutes – fabianmoronzirfas Jul 01 '16 at 15:25
0

I think for add function , need this global variable .So here I check for undefined condition for call function second times without parameter !

function fn1(f) {
  if(typeof f != "undefined") this.fn1.add = f;
  this.fn1.add();
  console.log('fn1');
}

function fn2(f) {
  if(typeof f != "undefined") this.fn2.add = f;
  this.fn2.add();
  console.log('fn2');
}
function main() {
  fn1(obj.foo); // <= Should add function foo to fn1
  fn2(obj.bah); // <= Should add function bah to fn2
  fn1(); // Should output: foo and then fn1
  fn2(); // should output: bah and hen fn2
}

main(); 
Jack jdeoel
  • 4,554
  • 5
  • 26
  • 52
  • OP said "But I can't add additional code to the fn1 and fn2 functions. These are written by others." – Oriol Jul 01 '16 at 14:56
  • Thanks David. I think Arg0n found the way and Oriol is right. I don't have control over fn1 and fn2. Only their names. Unfortunately I'm on a train and have to move to the next one in a sec. Exciting – fabianmoronzirfas Jul 01 '16 at 14:59
  • What u mean you don't have control over fn1 and fn2 ? Without changing any code , what changes will you get ! – Jack jdeoel Jul 01 '16 at 15:03
  • The user can whatever he wants in there. Could be he uses my foo or bah or other functions and constants from obj. The content of the fn functions does not come from me – fabianmoronzirfas Jul 01 '16 at 15:14
0
  1. Find out all of the names of the functions that you want to convert to globals from the library you are importing.
  2. Make them global. If you want to make some fancy AST parsing thing that finds them all for you, I suppose you could do that. Or you could just use the console and work it out. Nonetheless you actually want this to be static as that helps you reason about the code that is produced, If it is dynamically produced then future upgrades to the library could brake earlier code, if you have global names clashing.

But the code only needs to look like this

var foo = obj.foo,
  bar = obj.bar;
foo()
bar()

Alternatively, you could acknowledge that having all your functions namespaced isn't such a bad idea, and that it isn't a complicated even for a beginner programmer to make an alias of the function they want to use. It helps them understand that it can be a good idea to keep their code organised, and helps them understand that functions and objects aren't copied when they are assigned to a new variable name they are just referenced.

var map = reallyLongLibraryNameThatNoOneWantsToType.map
//alternatively
var lib = reallyLongLibraryNameThatNoOneWantsToType
lib.reduce([1,2,3],function(a,b){ return a +b }, 0)

This is the best answer that can be given for this case. I know that it is annoying. All the options given above create more problems then they solve. Really ask yourself why exactly do I want to do what I have asked?

James Wakefield
  • 526
  • 3
  • 11
  • Hi James thanks for the comment. We actually are going your direction. We are making all the functions global. With all these answers I'm not sure which I should mark as the right one. – fabianmoronzirfas Oct 18 '16 at 15:38