0

I'm trying to override some object properties based on the properties of a parameter object.

This code does almost what I want it to do:

// This function makes an object, assigns the given
//   callbacks to it, then processes it.
function myFunction(callbacks) {
    // Create the object. It has 2 callbacks that I
    //   want to override.
    var myObject = new Foo();

    // In this example, assume Foo now looks like this:
    //   {
    //       resultText: "",
    //       onOpen: null,
    //       onClose: null
    //   }

    // Now assign all the callbacks to myObject based
    //   on their name.
    for(var callback in callbacks) {
        myObject[callback] = function() {
            callbacks[callback](myObject.resultText);
        }
    }

    // Now we process the object, which should set the resultText
    //   and also end up calling the assigned callbacks when they
    //   are supposed to be called.
    myObject.process();
}

// Here is where I call myFunction and specify the callbacks
myFunction({
    onOpen: boing,
    onClose: bang
});
// Assume boing and bang are functions that operate on resultText.

What I'm hoping to get is essentially this:

var myObject = new Foo();
myObject.onOpen = () => boing(myObject.resultText);
myObject.onClose = () => bang(myObject.resultText);
myObject.process();

except I want to specify it in a nice, parameterized way.

Given only 1 callback, this works fine. But given both of them, the variable callback in the for loop remains as the value it had in the last loop iteration. This means that at the end of it all, both onOpen and onClose point to the same thing, that is function() {callbacks[callback](myObject.text);}. Since they both are using this old last-loop-iteration value of callback, they both end up pointing to bang, instead of one to boing and the other to bang.

I really like this idea of taking in a keyed object with the function names, but is there actually a way to do this type of assignment? Basically I want to "freeze" the value of callback in the middle of the loop by using its actual name, not the variable itself.

nullromo
  • 2,165
  • 2
  • 18
  • 39
  • 3
    If I've understood correctly, this is a variation on something which trips up more or less every JS developer at some stage. See the question I've marked as duplicate for extended discussion, but the TLDR in modern javascript is just to change the `var` in the `for` loop to a `let`. – Robin Zigmond Oct 10 '19 at 21:58
  • That's it! I just didn't know how to phrase the question properly; "Closure inside loops" is perfect. Is there something I'm supposed to do to confirm that this is a duplicate? – nullromo Oct 10 '19 at 22:17
  • 1
    no, you don't have to do anything. If enough people vote for it as a duplicate then it'll get marked as such, but either way the question will hang around, your version of it could still be useful to people searching in the future! – Robin Zigmond Oct 10 '19 at 23:22

1 Answers1

1

The problem is with the closures; easiest way to fix is with let instead of var

for(let callback in callbacks) {
        myObject[callback] = function() {
            callbacks[callback](myObject.resultText);
        }
    }

Another way is to write with arrow functions as

Object.keys(callbacks).forEach(callback => {
  myObject[callback] = () => callbacks[callback](myObject.resultText);
});
Dhananjai Pai
  • 5,914
  • 1
  • 10
  • 25