Huston
Huston

Reputation: 140

Changing function's scope

I recently discovered with in javascript to execute a block with an object as the current scope. I'm curious if there is some javascript magic to do the same thing with a function and call it in a different scope.

Example:

var scope = { foo: 'bar' },
    foo   = 'baz';

function func(){
  return foo;
}

with(scope){
  foo;    // foo is 'bar'
  func(); // foo is still 'baz' 
}

In the example, with changes the scope of the variable foo, but the function still uses the scope outside of with where it was defined. Any suggestions for how to alter or rebind the scope of a function?

Upvotes: 2

Views: 87

Answers (2)

Benjamin Gruenbaum
Benjamin Gruenbaum

Reputation: 276286

In short - YES

With is special, it does dynamic binding. It creates confusing bugs which is why with statements do not run in strict mode.

However no one said we can't have a little fun. We can't change the closure variables the function has, however - we can exploit JavaScript's highly dynamic nature to read the function's source code and generate a new binding. It's not core language or generic, but for the case you asked it's certainly doable.

Here is a sample for the very basic case you asked (no nested closures, no parameters etc) . While you can't alter the closure, you can declare a new function that runs the same code of the other one. Let me make one thing very clear - this is a dirty hack here ;)

var scope = {foo: 'bar'},
             foo = 'baz';


var func = function func() {
    return foo;
}
// here, `foo` is closed over by the outer foo and it is 'baz'

document.body.innerHTML += " " + func(); // foo is still 'baz'

// foo is bar, since it's a different function here:
document.body.innerHTML += " " + withFunc({foo:"bar"},func); 

function withFunc(obj, func) {
    //declare a new function
    return new Function("obj", // with a parameter obj
          "with(obj){\n " +  // do `with` on that parameter
          "return "+func+"()\n}")(obj); // invoke and return original function 
}

http://jsfiddle.net/42YX4/

Upvotes: 0

Benjamin Gruenbaum
Benjamin Gruenbaum

Reputation: 276286

In short - NO

With is special, it does dynamic binding. It creates confusing bugs which is why with statements do not run in strict mode.

The function in your case closes over foo. Once the function is declared there is no way to change which foo it references.


If we have to be precise:

Section 10.4.3 of the language specification describes "Entering Function Code", the interesting thing here is:

Let localEnv be the result of calling NewDeclarativeEnvironment passing the value of the [[Scope]] internal property of F as the argument.

This happens after we figure this out and before we set the scope to localEnv we just found. Now what is this [[Scope]] ?

[[Scope]] - Lexical Environment - A lexical environment that defines the environment in which a Function object is executed. Of the standard built-in ECMAScript objects, only Function objects implement [[Scope]].

This means that the scope of the function is decided beforehand, and not based on dynamic context :)

Upvotes: 2

Related Questions