Is it possible to declare/call a function in such a way that outer scopes are not visible?

StackOverflow https://stackoverflow.com/questions/23094569

  •  04-07-2023
  •  | 
  •  

Question

Say you have the following code:

var something = "here";

function test(){
   console.log(something)
}

Obviously when you call test(), the console log will show here as it will go up through the scope chain till it finds something or reaches the end of the chain. But is there a way to clear out the scope chain so that it does not look for variables outside the local scope.

I know you can use with, when not using strict mode, to add to the scope chain, so is there a way to do the opposite, remove scopes from the chain or some other way to make outer scopes inaccessible.

The closest thing I could think of that would accomplish this would be to pass the function body to a web worker along with any arguments, and execute it in the worker and have the results messaged back. And of course that's extra overhead and a bit over complicated to achieve something like this.

Do not actually need this, or have a situation in mind of its use, but thought it would be interesting to see if it was possible or not.

Was it helpful?

Solution

As said in the question, the only way of I thought of doing this was with web workers. Jeremy J Starcher mentioned in the comments about using an iframe, which I didn't even think of till the mention. So an example of that is provided as well.

Web Worker Example

  • All data needed would need to be passed as arguments.
  • Browser would need support for
    • Web Workers (of course)
    • Structured cloning, used in this example
    • Transferable objects, not used in this example
    • If neither above are supported would require the (de)serialization of arguments to strings
  • Requires use of a callback as worker is async (use promises instead?)
  • Might be modifiable to allow binding the function to some context, but that would require the context to be able to be passed as an argument, have not tried this

Main javascript

(function(window){
    'use strict';
    function respond(oEvent){
        this.cb&&this.cb(oEvent.data);
        this.worker.terminate();
    }
    window.sandbox = function(fn){
        return function(){
            var worker = new Worker("sandboxWorker.js");
            worker.onmessage = respond.bind({
                worker:worker,
                cb:[].pop.call(arguments)
            });
            worker.postMessage({
                "fn":fn.toString(),
                "args":[].slice.call(arguments,0)
            });
        };
    };
})(window);

sandboxWorker.js

onmessage = function (oEvent) {
    var fn = eval("("+oEvent.data.fn+")");
    var res = fn.apply(null,oEvent.data.args);
    postMessage(res);
};

Test

//Assume jQuery has been included
function maliciousAdd(a,b){ 
    jQuery = function(){ alert("No it doesn't fix everything"); } 
    return a+b;
}
var sandBoxed = window.sandbox(maliciousAdd);
sandBoxed(1,2,function(result){
    jQuery(document.body).css("background","#3F3");
    console.log(result);
});

JSFiddle Demo

IFrame Example

  • If same-origin source, function could still access parent window etc.
  • If using different origin, use of postMessage would have to be used
  • Do not need to (de)serialize/clone arguments, in the case of same-origin iframe
  • Synchronous, but could be made to be async as well.

Javascript: modified to use Function to generate inner sandbox function as suggested by Felix Kling

window.onload = function(){
    var frame = document.createElement("iframe");
    frame.src = "";
    frame.style.display = "none";
    window.document.body.appendChild(frame);
    var win = (frame.contentWindow) ? frame.contentWindow : (frame.contentDocument.document) ? frame.contentDocument.document : frame.contentDocument;
    window.sandbox = function(fn){
        return function(){
            var args = [].slice.call(arguments);
            win.location.reload();
            win.sandbox = win.Function(
               'fn','args',
               'fn = eval("("+fn+")"); return fn.apply(this,args);'
            );
            win.document.head.appendChild(script);
            return win.sandbox(fn.toString(),args);
        };
    };
};

JSFiddle Demo

Docs

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