0

I have a GeneralWrapper object that calls the statically-defined functions in the Library1 and Library2 objects.

The aim is that by calling GeneralWrapper.someFunc(), this will also call Library1.someFunc() and Library2.someFunc() without me having to explicitly create a function in GeneralWrapper called someFunc.

I attempt to implement this in the __preamble method below:

var GeneralWrapper = {

    __modespopulated: false,

    __validmodes: {     // All 3 of these
        spoonFunc: 1,   //  functions exist
        knifeFunc: 1,   //  in Library1 and
         forkFunc: 1    //  Library2
    },

    __switchMode: function(funcname){

        if (funcname in GeneralWrapper.__validmodes){
            console.log("calling function", funcname)

            GeneralWrapper.__preamble()

            Library1[ funcname ](); // Call mode in Library1
            Library2[ funcname ](); // Call mode in Library2
        }
    },

    /* Attach valid modes to General Wrapper at runtime */
    __preamble: function(){
        if (!GeneralWrapper.__modespopulated)
        {
            for (var mode in GeneralWrapper.__validmodes)
            {
                GeneralWrapper[mode] = function(){
                    GeneralWrapper.__switchMode(mode)
                };
            }

            GeneralWrapper.__modespopulated = true
        }

        GeneralWrapper.__otherprestuff();
    },


    __otherprestuff: function(){
        // Stuff
    },

    funcThatAlwaysGetsCalled: function(){
        GeneralWrapper.__switchMode("forkFunc");
    }    
}

var Library1 = { 
     forkFunc(){console.log("Lib1","fork")},
    spoonFunc(){console.log("Lib1","spoon")},
    knifeFunc(){console.log("Lib1","knife")}
}

var Library2 = { 
     forkFunc(){console.log("Lib2","FORK")},
    spoonFunc(){console.log("Lib2","SPOON")},
    knifeFunc(){console.log("Lib2","KNIFE")}
}

// Okay, let's initialise the object
GeneralWrapper.funcThatAlwaysGetsCalled();

For some reason calls to GeneralWrapper.spoonFunc() and GeneralWrapper.knifeFunc() always defer to the Fork output.

I imagine the problem stems from the anonymous function assignment on the GeneralWrapper[mode] = blah line where JS treats it as the same function each time, but I don't know how to get around this.

Please advise.

2 Answers 2

1

Solution:

Change this line:

for (var mode in GeneralWrapper.__validmodes)

into this:

for (let mode in GeneralWrapper.__validmodes)

Explanation:

what happens in your code (when binding functions in __preamble's loop) is that you create an anonymous function, which is totally fine. The problem is, your anon function has received the mode as a reference to local variable, so it's value is not automatically cloned but rather accessed at runtime. The main problem is that you've used var keyword, which means "hoisted variable" (it gets declared at the top of the function it was defined inside, even if it's somewhere in the middle of your function's code). In this scenario, you need a "block-scoped" variable, which will be bound to each loop iteration separately.

You can read more about variables hostings on MDN:
var at MDN
let at MDN

One thing you have to know - let was introduced in ES2015, so if you worry about backward compatibility with older browsers, you either have to use Function.prototype.bind or IIFE

Sign up to request clarification or add additional context in comments.

1 Comment

wow I never knew! I'm using strict mode so I just assumed that var scope was always locked tight. Thanks
1

One potential problem here is that you're creating functions inside a loop which can lead to some performance problems or unexpected behavior.

I'd replace:

for (var mode in GeneralWrapper.__validmodes)
{
    GeneralWrapper[mode] = function(){
        GeneralWrapper.__switchMode(mode)
    };
}

with:

for (var mode in GeneralWrapper.__validmodes)
{
    GeneralWrapper[mode] = GeneralWrapper.__switchMode.bind(this, mode);
}

Which should solve the problem at hand.

1 Comment

thank you, I went with the other answer for the reading material

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.