Sunday 4 November 2012

Alternates to the evil EVAL() in javascript

I am interrupting the Design Series of post for a quick JavaScript post that comes out of a question asked on the JavaScript forum on LinkedIn about alternates to the eval() function in JavaScript.

The most pertinent reasons for not using eval() are:-

1. Security - it leaves your code open to a JS injection attack which is never a good thing
2. Debugging - no line numbers which is a PITA
3. Optimization -  as until the code to be executed is unknown, it cannot be optimized.

Sadly EVAL is way to easy to use and as a result we see it all to often in places we shouldn't really see it and this can leave the door open to some nere-do-well making a mockery of the rest of your obviously wonderful code.

So what to do to avoid using eval()? Well alternates to the the 3 main areas I have used EVAL in the past are listed below.

1. When working out a bit of an object to change for example something like this

eval('document.'+IdName+'.style.display="none"');

Now I am not suggesting anyone WOULD do this given the tools available but I have come across code like this in older applications written by people just starting out in the wonderful world of JS.

document[IdName].style.display= 'none';

or

document.getElementById(IdName).style.display='none';

are both much safer and a lot faster.

2. Getting JSON that has been returned by AJAX calls to the server into the DOM, like this snippet of jQuery is doing.

$.ajax({ url: "getdata?openagent&key=SMCD-98LJM",

              success: function(data) { eval(data),
                                        doSomethingInteresting()
                                      }
        })

This will work but a more satisfactory way would be to use the JSON data type in the ajax call

$.ajax({
  url: url,
  dataType: 'json',
  data: data,
  success: *callback*
});

alternately use the $.getJSON() jQuery shorthand function.

If you don't use jQuery (or dojo which has an equivalent) you can use the powerful JavaScript JSON object.

var myJSONData = JSON.parse(data, reviver);

Where DATA is the the JSON data string and REVIVER is an optional function that will be called for every KEY and VALUE at every level of the final resulting object. Each value will be replaced by the result of the REVIVER function. For example this can be used to change strings in the JSON text into JS Date() objects.

3. The thorny and potentially very insecure loading and execution of Code Blocks, probably the most common use of eval() function in Javascript. Anywhere you allow code as text to be passed and run in the browser is prone to attack, it is much better where possible not to do this. When you absolutely have to do it then I would say using the Function constructor is less risky than a bold eval() call.

var myCode = "... your JS code ..."
var doSomething = new Function(myCode)
doSomething.call()


This will have the same effect as EVAL() but is less prone to being found as an avenue of attack for the forces of chaos on the internet. ** Note** you can also use the constructor format below to pass parms to the code block.

var doSomething = new Function("..var1name..","..var2name..", etc .. "..myCodeString..")

On a side note but related note, when you create a normal JS function construct, the definition does not have to appear at the start of the script (though it is usually best to do so for the sake of clarity). It can even be defined after the the code that calls it. In most cases, no matter where you choose to define your function, the JavaScript engine will create the function at the start of the current scope. BUT and it is an all caps BUT if you need to construct a code block conditioned on an IF, like this
if( someThingIsTrue )
 {
 function doSomeThingWonderful.call() { .... }
 }


Mozilla based browsers will allow it, but most others do not as they will always evaluate the function, even if the condition evaluates to false. So do not try to declare functions in the way noted above. Declaring functions inside these statements is possible in all current browsers using assigned anonymous functions, so it is better to do it this way.

var doSomeThingWonderful;
if( someThingIsTrure ) {
  doSomeThingWonderful = function () { --CodeBlock A--};
} else {
  doSomeThingWonderful = function () { --CodeBlock B--};
}
doSomeThingWonderful.call()

There are other reasons you need to evaluate objects or code  but generally there are ways around most of them that whilst potential slower to process are more secure and sensible.



     

No comments:

Disqus for Domi-No-Yes-Maybe