eval has always been the dark horse of the apocalypse in JS.
But there really is only one thing that still needs to be done to make eval truly unjustifiable and finally get rid of it for good!
A new construct: āreflectā
Today the only true equivalent of Javaās Class.getClass(ānameā) is eval(ānameā).
But eval can do anything not just new instances!
My proposal is simple:
class Foo{
constructor(bar)
{
console.log(bar);
}
}
let fooName = "Foo";
let foo = reflect fooName;
if(foo){
new foo("bar");
}
// prints: bar
There are many cases where you donāt know the class of an object beforehand the most basic of which is that of a driver and a driver factory, you code against a contract not a hardcoded class name!
Reflecting should be restricted to constructors only, providing a safe entry point without weird side usages.
Main use case would be drivers and driver factories.
The simplest example I can give is mysql vs D3 on Cloudflare, we have tables in both, both work with SQL and parameters so the signatures of the functions are the same and the sqls are rather basic so they are cross compatible. A service would need to write to a speciffic DB and it knows it just by name, it requests from the factory a name by a string say DBFactory.getConnection(ānameā). The factory would load the config from either an ini or a json and then instantiate the appropriate driver as indicated in the config, and āestablish the connectionā (not a real connection in the case of cloudflare but you get the gist.)
Iām left to either a lot of boilerplate to do the mapping or with eval (in environments that allow it)
I'm also struggling to understand what exactly the proposed syntax does. Is there a local myFoo variable defined above? And the class that created myFoo is what's being returned from this reflect syntax?
I can't conceive of why anyone would ever use eval in that scenario in the first place - that assumes they're using globals, which have been a bad practice for the better part of two decades, almost as long as using eval.
Indeed, but the fact that we still have eval after all the new tooling tells me there is still a considerable amount of evalāed code in the wild.
Also a few mentions here.
1 - reflect should only have access to the lexical scope that new does (no cross module or anything else) - basically if you canāt instantiate you canāt āreflectā.
2 - minifiers would need to preserve the name of the class along with all methods if said class is to be externally usable. they could have something like
let foo = reflect fooName
// becomes after minification:
let a = r[b]
// where:
let r = {āFooā:x};
// and x is the new name of the class.
3 - if reflect fails it fails quietly as undefined - no error - or minimal error handling like TypeError or RefferenceError.
The fact that eval is still around isn't because we don't have good alternatives for these sorts of scenarios, it's because we need to preserve backwards compatibility - eval can't be removed. Also, eval isn't always evil, there are legitimate uses for it.
Anyways, my concern with the above code examples, it that I find the boilerplate to be very useful when it comes to code comprehension - it tells me, a future maintainer trying to understand the code, exactly what can and can't be used as a string name for this sendSqlCommand function, and it ensures the caller never accesses anything else from your moduleās scope that you didn't intend them to access.
The moment you use reflect like that, you greatly weaken your module's encapsulation and make it so the caller could access things you didn't intend them to access. For example, say you wanted to import a third driver, not because you wanted users of sendSqlCommand() to have access to it, but for some other purpose. How would you do so without automatically granting sendSqlCommand access to this third driver?
Code editors would also have a bad day with this. They like to gray out variables that are unused, but would be completely unable to do so the moment you use the reflect syntax - they have no way of telling if an unused variable is actually used via reflection or not.
Reflect should only have access to the inherited scope, so if you can instantiate you can reflect, if you canāt do one you canāt do the other. How would it happen, could you show me some example code?
Since JS has no actual classloader that can insulate contexts every insulation is just coincidental till someone does something stupid, but I donāt really see how reflect can aid in that.
I donāt understand why eval canāt be removed, I mean I used to use it 20-25+ years ago too (fun times), but nowadays it makes no sense. If you really need eval you either need webgl (webcl? if itās still alive) or just modern js in general from anons, closures, promisses, proxies, reflection to modern apis etc⦠or reflect
If we would have a true oop class model with classloader insulation subclasses etc and such Class.getClass(ānameā) would be completely natural, but as it stands reflect seems like the obvious extension to me.
We all dislike eval, but JavaScript has to maintain eternal backwards compatibility. The Committee and the big engines will not make any changes to the language that suddenly break existing applications with real-life users.
There have been a few exceptions: if the Committee can reach out to the maintainers of every single application that a new feature would break, and it asks them to change their application, so that the application doesnāt break for existing users after the language change, then maybe the new feature can be added. Thatās a lot of ifās.
Oftentimes this is not possible (look up āSmooshgateā for an example). And use of eval is one of those cases. eval is still widespread in used applications. That ship has sailed; we can only try to make it gradually less powerful or common in backwards compatible ways.
All web standards, such and HTML and CSS, are like this. āDonāt break the webā. (With varying degrees. Some web standards do take into account whether a feature is used by practically nobody and then cautiously remove it. JavaScript is not one of them.)
We can sometimes formally deprecate features in web standards, and maybe eval should be one of them. but one of JavaScriptās goals is to maintain eternal backwards compatibility. Yes, this has trade-offs, but itās a foundational stability principle that millions or billions depend on.
With that said, thank you for your enthusiasm and welcome to this forum. eval is an important topic.
I donāt know why people are so keen to remove eval. Given the high tendency for JS language design to copy other languages, I would expect the lack of eval to be odd for JS since itās in virtually every single interpreted language. PHP has it; Python has it; Bash has it; Ruby has it; R has it; and the list could go on. eval can be (rightfully) discouraged by style guides and security advisories, but the machinery being available is useful and natural.
import { MySqlDriver } from 'mysql-driver';
import { D3Driver } from 'd3-driver';
import { MockDriver } from './MockDriver'; // <-- I added this import
export function sendSqlCommand(driverName, command) {
const Driver = reflect driverName;
const myDriver = new Driver();
...
}
Because I added the import to this MockDriver class, now it becomes valid to call sendSqlCommand(āMockDriverā, ā¦), as that would reference one of the local, instantiable variables in the module. Even if I didnāt intend to add āMockDriverā as one of the allowed strings for sendSqlCommand(), it automatically is, just because I added a new import to the module.
Unless Iām still mis-understanding how this works. (Iām not familiar with this Class.getClass(ānameā) function you keep speeking of in Java - and when I try to google it, all I come up with is instance.getClass(), which seems to be different from what youāre talking about - that just returns the class that created the instance - the equivalent in JavaScript would be instance.constructor).
True, Classloaders donāt make sense for a language that run singlethreaded and siloed like js, that is why you can also not have true isolation, basically unless you have security siloing like iframes do for instance nothing you load can be truly insulated.
In my experience if you have gotten to the point of I need eval⦠you already are many architectural mistakes in and yes at that point in time eval may be the only solution that doesnāt get you fired over a months long refactoring. And I agree in days past eval was not only nice to have, was necessary because we didnāt have any advanced tools, but in the land of modern js with proxies with reflection with type casting with promises on one hand and the webgl, xml/xsl and many other modern apis implemented in JS there is simply no NEED to use eval, you can still find a shortcut using it but usually evaluating your options and using ānormalā JS is usually much readable safer and normally faster too. This applies to any mature language, eval is the silver bullet for new languages to say well if you canāt do it with what you have you can always use eval⦠itās the āothersā section of the computing world.
Not breaking the web is such a bad policy, and if that would have ever been truly the case, why did we obsolete all the quirky stuff IE was doing? why was active-x tossed? with construct? It has nothing to do with actual programmers needs and maintainability and everything to do with big corp politics. Big corporations have their stacks built on ancient wobbly code and upgrading to modern times was always expensive! But in the days of AI? Today you could use various linters and static analysis tools to highlight the bs and then have ai propose fixes. I mean I wouldnāt want to rewrite a 250k code base I wrote over 20 years ago, sure, but I could probably rewrite it today with only 10k lines and with ai it would only take me the better part of a month and make it make sense and be 1000x more maintainable. What would be the incentive of me doing that without if everything is forever backwards compatible.
I mean, critical infrastructure already runs frozen builds on frozen deployments - those NEVER change whatever the committee will ever decide, and thatās fine! Websites on the other hand have been broken all the time! Especially in the transition from IE ā chrome websites would stop working right all the time, and it was ānormalā.
What drives me nuts is that classes had the clear cut new start chance we were waiting for for decades, and what did we get? another halfbaked mess that we now have to āforevermoreā live with. At this point try explaining to juniors how the inheritance schema works in javascript without sound totally insane! And then you see coding guidelines like: never use āthisā⦠Even hoisting, heck even the execution stack explanations make more sense.
So yeah, I get it, backwards compatibility is nice and all, but you have to set time limits, if a code isnāt getting updated it means nobody needs it! Because if someone needs it they would take up the maintenance and upgrade there of even if original maintainers and authors are no longer in the picture - or option b just go with another similar library, or roll your own. And if now the committee would say in 5 years eval will be completely taken out of the language and never supported again. Yes there would be initial uproar and people would panic for a while, but at the end weād all sleep better at night.