A Large-Scale Study of the Use of Eval in JavaScript Applications (2011) [pdf]
the.gregor.institute
the.gregor.institute
var ev;
eval("ev = control." + eventType + ";");
eval("control." + eventType + " = func;");Another cool thing that you find is JavaScript is the "with" keyword. A lot of people don't even know it exists. I think Chrome's DevTools uses it along with eval. When you type something invalid, you can see stuff like:
with (typeof __commandLineAPI !== 'undefined' ? __commandLineAPI : { __proto__: null }) { whatever }
Good, it's fantastically broken and shouldn't be used.
It might be terrible to use in the vast majority of situations, but it works and it works great for those few situations that it's extremely useful for. Blacklisting something like that as a hard rule ends up doing much more harm than good in my opinion.
This is why blanket black-listing (or white-listing in cases where it is possible) is a better option because, similar to Murphy's law, if something (like eval) can be misused it will be.
if someone wants to do something "bad" in a language, it's better to teach them why it's bad than to blacklist certain parts of it.
I've seen developers tell me that eval is bad, but they found a way to do what they want by using `new Function()` (which takes a string of code and makes it into a function that can be executed...)
Teach why these things are a good idea to avoid, teach why it's dangerous, teach scenarios where it's tempting but there is a better way, but don't disallow their use entirely. We wouldn't be developers if we just gave up the first time someone told us that we couldn't do it that way.
To enforce rules like this, the best practice I would say is to add JSLint or ESLint with a strict rule set on a pre-commit hook and then refuse any commits / merges that don't pass the static analysis of those tools.
An eval example i've seen recently was about sending functions to web-workers. The only way to allow code to run in a web worker without a separate file is to convert a function to a string and eval the code in the web-worker side. So i could massively over complicate my build system, duplicate code in a bunch of places, and near-double the size of the downloaded code to get it working without eval, or i could setup a simple eval system and have it run a function passed in as a string.
Yeah, you shouldn't be using eval to decide if the number entered was 5 or 6, but saying it's disallowed from the codebase entirely is silly. Even a policy like "any code that uses eval must be approved by X, Y, and Z before they will be allowed in the codebase" is much more sane.
Maybe there are minor use cases where it's helpful, but for 99% of cases it's just plain bad. Why publicise it if it's not very useful, broken and will cause even experienced developers (not to mention new ones) debugging headaches?
Bring them out in the open, talk about why they are useful (or were in the past) and why they are dangerous, talk about what problem they were meant to solve at the time and what the better ways there are now to do it.
I'm not going to mourn the loss of `with` if it gets removed from the language, but i'm not going to shove my head in the sand and pretend like it doesn't exist either.
See the tweet by Brendan Eich (inventor of JavaScript) on the with: https://twitter.com/BrendanEich/status/68001466471817216
From here on, this rest of this comment is probably of little interest, but for those curious about what I mean when I say "reason about" a program let me try to explain in more detail.
Programming in an imperative language (i.e. not a functional programming language) involves a series of steps which make changes to the state of the program's data. We can't desk check interesting programs by working through all interesting steps by hand (or otherwise there is no point in having a computer do it); there are typically just too many steps and iterations. So how do programmers do it?
One reasons about the state of a program by examining and understanding how it is affected by each statement. So, for example, in a while loop like:
while x != y do
x = f(x)
end
t = x
the loop does something and even without understanding exactly what the side effects of f(x) are, we are able to reason about the properties of x once the program reaches the point where x is being assigned to t. We know that x equals y and so after the assignment statement we can assert that t, x and y are all equal. Now consider the same while loop with only a slight change: while x != y do
x = f(x)
end
L: t = x
Now the assignment of x to t has a label and can be the destination of a goto statement (I'm using an example from the bad old days when goto statements could take you anywhere in a program). What can be said about the relationship between t, x, and y after the assignment? It depends on anyplace in the code that can goto to label L. We can no longer reason about the code by examining it statically. We have to understand the global execution and when and where the goto can happen and what the value of x and y might be at each of these potential goto statements during the execution of the program. We've lost the ability to reason about the code in a systematic way by simply looking at it. We would have to consider every possible way the execution of the program could change x or y and then jump to L.Certain programming patterns make understanding programs difficult. Unbridled goto's were an early source of such problems. Now, most languages and programers restrict how they use goto to patterns that are safer because they don't make reasoning about the programs much more difficult. Dynamic scoping (around since the early days of LISP) makes reasoning about a program's state more difficult than lexical scoping; consequently, most programming languages now eschew dynamic scoping in favor of lexical scopes (that is scopes for variables determined by the structure of the source code rather than the dynamic call patterns during execution).
The JavaScript "with" is deprecated because it can be applied to an object that is unknown until the program actually runs. The body of the with is executing in this dynamically determined scope so we can't really reason about what is going on while looking at the code.