Reputation: 140
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
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
}
Upvotes: 0
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