kojiro
kojiro

Reputation: 77079

Capturing a closure in JavaScript after-the-fact

I have a 3rd party JavaScript application that declares several global functions and needs some variables to be defined for those functions to work. I would rather not define those variables in the global scope. Here's an example:

Suppose you have a globally defined function foo that prints out the value of an externally-defined variable named x.

function foo() {
    console.log(x);
}

I think the only way for x to have a value within foo is if x is also a global. I hope I'm wrong. What I would really like to do is this:

(function () {
    var x = 'someValue';
    var bar = magicallyFixSoXIsntGlobal(foo);
    bar();
}());

I guess I could do:

(function () {
    var x = 'someValue';
    var bar = Function(foo.toString());
    bar();
}());

But that seems pretty much like eval. (If it comes down to that, I'd rather have global spam than use eval.)

Is there any way to "fix" the global functions so they can refer to the enclosed values?

Upvotes: 0

Views: 84

Answers (3)

st-boost
st-boost

Reputation: 1907

The obvious solution is to modify foo to take x as a variable. But, assuming you can't / don't want to do that, I can think of two options:

  • declare x as a global variable.
  • Wrap foo's declaration in a closure.

The first will certainly make foo work, but please don't take Rob W's approach. Remember that the reason people tell you "don't clutter the global namespace" isn't because they're worried about unused variables hanging around (that can be a problem, but it's not specific to the global namespace). It's because having multiple functions using the same namespace increases the chances that one will overwrite the other's variables. Here's how you would use the global x without bludgeoning the rest of your code.

!function(x, exists) {
  window.x=10;
  foo();

  if(exists)
    window.x=x;
  else
    delete window.x;
}(window.x, 'x' in window);

The second option shouldn't be too hard if foo is currently just a function declaration within your code. In that case, change it to:

!function() {
  var x=10;
  function foo() {
    [...]
  }
  // foo can only be called within this block
}();

Or if foo must be global, but you still don't want x to be global:

!function() {
  var x=10;
  window.foo=function() {
    [...]
  };
  // now foo can be called anywhere, and will still use the x declared above
}();

But if foo is included via an external script then it gets more complicated, and I don't think you could use this method without modifying foo. (You could also use ajax and eval, or just eval, but I think you're right in avoiding that function.)

Upvotes: 1

Raynos
Raynos

Reputation: 169383

This is what function parameters are for

function foo(x) {
  console.log(x)
}

You could hack it horrible if you wanted by converting the function to a string and creating a new function which accepts parameters, but that's dirty as hell

Upvotes: 0

Rob W
Rob W

Reputation: 348972

Assign a value to a window's property, call the function, then use the delete operator to truly remove the variable.

window.foo = 'bar';
magic();
delete window.foo; // Because `foo` is defined without `var`, it can be deleted

Upvotes: 1

Related Questions