Question

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.

Was it helpful?

Solution

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.

OTHER TIPS

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.

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...

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top