One of the supposedly evil things that is available in jQuery is eval. The type of eval that is available is of the global variety. In other words globalEval provides the ability to execute JavaScript code from a string in the global scope. Most people don’t realize it, but eval by itself evaluates a string in the scope from which the call is made. By finessing the code a little, you can actually make a function that will always execute your code in the global scope:


// By Chris West - MIT Licensed
globalEval = (function(global, realArray, indirectEval, indirectEvalWorks) {
  try {
    eval('var Array={};');
    indirectEvalWorks = indirectEval('Array') == realArray;
  } catch(err){}
  
  return indirectEvalWorks
    ? indirectEval
    : (global.execScript
        ? function(expression) {
            global.execScript(expression);
          }
        : function(expression) {
            setTimeout(expression, 0);
          }
      );
})(this, Array, (2,eval));

Still you may be a little wary about this actually working differently from the way eval normally works. Check out the following example:


function fn() {
  // define a inside
  var a = "inside fn";
  
  // show the value of the version of a inside of fn
  eval('alert(a)');
  
  // define b inside fn
  eval('var b = true;');
}

// define a outside as well
var a = "outside fn";

// show the fn version of `a` and define `b` inside fn
fn();

// prove that eval evaluates expressions in the local scope
alert("b is " + (typeof b == "undefined" ? "un" : "") + "defined");

If you were to test out the above code, you would see that first "inside fn" would be alerted. Next "b is undefined" would be alerted to prove that even though var b = true; was evaluated inside of the fn function, the definition wasn’t applied to the global scope.

Now if we were to use the same code, but replace eval with our newly defined globalEval function something different would happen:


function fn() {
  // define a inside
  var a = "inside fn";
  
  // show the value of the version of a inside of fn
  globalEval('alert(a)');
  
  // define b inside fn
  globalEval('var b = true;');
}

// define a outside as well
var a = "outside fn";

// show the fn version of `a` and define `b` inside fn
fn();

// prove that globalEval evaluates expressions in the global scope
alert("b is " + (typeof b == "undefined" ? "un" : "") + "defined");

Assuming that globalEval is defined, you would see that first "outside fn" would be alerted. Next "b is defined" would be alerted to prove that even though var b = true; was executed from within fn the definition was placed in the global scope.

Indirect Eval

As you are looking at the code, one question that may come to mind is why use (2,eval)? Firstly, the number two has little to no significance. I could have put anything there. The reason I this code is used is to basically provide an indirect version of eval. This expression could actually be replaced with window.eval, but I wanted something shorter and thusly used (2,eval) instead.

Browser Support

This function has been successfully tested in IE6+, Firefox, Google Chrome, and Safari. The function as it is written will act differently in IE and other older browsers than it will in more modern/standard browsers. The reason for the differences is that window.execScript is used by IE and this function doesn’t return anything. Other browsers that don’t have window.execScript will default to using setTimeout which, interestingly enough, does execute code (when passed as a string) in the global scope in non-IE browsers. On the other hand, eval returns the value of the last expression. Of course, if you need to globalEval function to always work the same way in every supported browser, I would encapsulte the returned function definition in another function which doesn’t return anything.

Final Notes

  • This post was a result of research, of which much of the details were derived from this page.
  • Unfortunately I was not able to get this to work on the desktop in JScript.
  • This version of eval is no less dangerous if you are planning &emdash; if you are planning to execute code this way, BE SURE THAT THE CODE IS WHAT YOU WANT!!!
  • If/when the next version of jPaq comes out, this will become part of it.
  • The downside of falling back to setTimeout is that this code may be delayed by other code that hits the queue due to a previously set timeout or interval (although this is unlikely).
  • Another fallback which was used in jQuery exists:
    1. Create a SCRIPT element.
    2. Add the code as a text to the SCRIPT element.
    3. Add the script element to the HEAD or BODY.
Categories: BlogJavaScript

Leave a Reply

Your email address will not be published. Required fields are marked *