2

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?

Huston
  • 150
  • 7
  • 3
    Just FYI: _"Use of the with statement is not recommended, as it may be the source of confusing bugs and compatibility issues. [...] Using with is not recommended, and is forbidden in ECMAScript 5 strict mode."_ -- [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with). – elclanrs Sep 25 '13 at 23:08

2 Answers2

2

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 :)

Community
  • 1
  • 1
Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
  • Related and interesting, but deprecated and certainly not a part of the language - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/Parent – Benjamin Gruenbaum Sep 25 '13 at 23:30
  • It's interesting that "this" is set before the scope. Considering "this" is easily modified with apply/call, I'd expect scope to be set first since it's static. Thanks for the thorough explanation! – Huston Sep 26 '13 at 16:29
0

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/

Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
  • Thanks! This is exactly what I was looking for. I figured it was a bad idea to actually do. It was more of a curiosity about whether it was possible. I completely forgot about the function constructor, that's a great solution. – Huston Sep 26 '13 at 16:24