views:

635

answers:

4

Given the following snippet of javascript in a scope:

var x = 10;

function sayx() {
  alert(x);
}

sayx();

You would of course expect a message box printing '10', you could do multiple function nesting to interact with how 'x' is determined, because when resolving what x is the environment walks up the scope chain.

You can even do a level of 'recompilation' with eval to inject new scopes at runtime.. for example:

var x = 10;

function sayx() {
  alert(x);
}

function wrap(f) {
  return eval('(function() { var x = 20;(' + f + ')(); })');
}

wrap(sayx)();

This works because the function will have its toString function called which will return the 'original' source.. thus we essentially create a wrapped version of the function that has a new scope that overrides x.. the result will be an alert box that prints '20' and not '10'.

However, on the surface this could appear to work but the scope chain is broken, the next item in the chain is no longer the same because the function 'f' is now defined at a different location.. even worse is that the scope chain it has inherited could contain many references that the calling function shouldn't have access to.

So, is there a more supported, workable way to inject a scope item? something like:

function withScope(f, scope) { ??? }

---

var x = 10, y = 10;

function dothemath() {
  alert(x + y);
}

var haxthemath = withScope(dothemath, { x: 9000 });
haxthemath(); // 9010 not 20

I'm guessing the answer is 'no', some may argue there are 'security' issues with such scope injection, but considering you can do the trick anyway (albeit severely broken) I don't think it is..

The benefits of this would be that you can essentially fake your own pseudo variables.

Thanks in advance.


Edit, just the clarify a little, imagine I wanted to 'withScope' a function that had the following scope chain:

Window - window properties
Object - { var x = 10 }
Object - { var y = 5 + x }

I would like to be able to get a function back that effectively had the same chain + a scope I provide.. ie:

withScope(somefunc, { foo: 'bar' })

Would give me

Window - window properties
Object - { var x = 10 }
Object - { var y = 5 + x }
Ext scope - { foo = 'bar' }

All prior standing variables would be found because my extended scope doesn't say anything about them.

A: 

the answer I think you're looking for is the built in "with" statement.

However, I wouldn't reccomend using it, as it is deprecated, and will very likely not exist in ecmascript 6

The only other way I think you could do this sort of thing is from inside the host application itself, manipulating the javascript environment from the outside. Or, if you're using rhino, you can actually do that from inside javascript, because the link between Java apis and Javascript is just that seamless in rhino. When steve yegge first pointed that out it blew my mind. Manipulate javascript from outside of javascript, but from inside javascript! It's genius!

If you're stuck inside a browser environment, perhaps you can use a javascript interpreter written in javascript, such as narcissus.

Breton
I don't think the with statement lets you infer scope in the way I want, but yea this may be something that is out of scope for javascript.. it just seems frustrating that you can completely blow away the closure scope and provide your own (causing who knows what mayhem), but you can't just add a little bit on top of the existing closure.. legit scenarios like adding your own pseudo variables would be really useful.
meandmycode
The with statement takes an object and inserts it directly at the head of the existing scope chain, as a scope, preserving the rest of the scopes. They've dropped it because having to support with makes it difficult to optimise javascript interpreters.
Breton
Yep however it inserts the scope to the head of the scope it is defined in, whereas I'm looking to create a copy of a function with a new scope to the scope it had.
meandmycode
+3  A: 

If you are refering by scope to the local variables in the function and/or the closure, I think the answer is no.

You can change the scope of the this keyword by using functionName.call(scope, arg1, ...) or functionName.apply(scope, [arg1, ...]); This could be used together with prototypes to create similar chains as you describe - if something isn't found in the object's own properties, it's looked up in its prototype. If the property is not there, the next prototype in the chain is used and so on.

Jani Hartikainen
Yep this is about closure context vs the call context.
meandmycode
Thanks Jani, I'm going with your answer "no", I didn't think there was a way but I certainly thought it was worth throwing out there in case any interesting 'hacks' existed..
meandmycode
A: 

Maybe use javascript's built in toString method for functions?

function withScope(f, scope) {
    var toExec = "";
    for (var prop in scope) {
        toExec += "var " + prop + " = scope['" + prop + "'];"
    }
    toExec += "f = (" + f.toString() + ")";
    eval(toExec);
    return f;
}
var x = 10;
var f = function() {
    console.log(x);
};
withScope(f, {x: 20})();

This seems like a bad idea though...

Caleb
Yep, the second code snip does this trick (just simpler for the purposes of demo), it works in that the closest scope works as expected, but the scope chain is now completely different and doesn't chain on from the original one
meandmycode
Oh sorry I didn't read the question closely enough...
Caleb
A: 

You if want to play around with scope you might enjoy the let statement provided in Javascript 1.7. Recent versions of Firefox support let.

pr1001