JavaScript Ruminations

I have used JavaScript of and on for many years but in the last three years I have used it seri­ous­ly for build­ing a large client-​side web appli­ca­tion. During that time I have push many of the bound­aries of JavaScript and uncov­ered some fun­da­men­tal issues with the language.

One of the most com­mon com­plaints about JavaScript is that its object sys­tem is bro­ken. This is almost always becuase of a mis­un­der­stand­ing of what the lan­guage is. JavaScript is not an object-​oriented lan­guage, it is a pro­to­type lan­guage. What does this mean? It means that I can change the “object hier­ar­chy” of an exist­ing object at run­time by chang­ing the objec­t’s pro­to­type. This is by design and isn’t one of the real problems–it just requires a dif­fer­ent way of thinking.

No, JavaScript has a much larg­er prob­lem and it is one that can­not be fixed with­out chang­ing the lan­guage. Because it is a pro­to­type lan­guage, the var­i­ous stages of the build process (the com­pil­er, the run­time engine, the code emit­ter) can­not know what a par­tic­u­lar token means until the token is executed.

Let me explain: when you enter a token like foo into the code this is a direc­tive to the run­time sys­tem to look up the cur­rent def­i­n­i­tion of the token at the moment it is exe­cut­ed. The sys­tem can­not deter­mine that the token has the def­i­n­i­tion of say 123 and use that infor­ma­tion the next time the code is exe­cut­ed because the val­ue could change. Even the type could change. Now if this is a true vari­able like the num­ber of items in a list then it works as expect­ed but if this is a ref­er­ence to a func­tion to call then the func­tion that is called can change from one exe­cu­tion to the next. Now I’m not talk­ing about the restrict­ed changes that OO allows like over­rid­ing and over­load­ing I’m say that there does not have to be any rela­tion between the first func­tion and the sec­ond; they can even have dif­fer­ent para­me­ter lists. This makes the pro­gram lit­er­al­ly impos­si­ble to rea­son about.

Let that sink in: it is impos­si­ble to rea­son about a pro­gram. If any func­tion invo­ca­tion can be changed any­where in the code to any oth­er com­plete­ly unre­lat­ed func­tion then you have no way of know­ing what func­tion is real­ly being called. One pop­u­lar way of “cus­tomiz­ing” a library class is to replace the exist­ing def­i­n­i­tion of a func­tion with anoth­er, pos­si­bly wrap­ping the orig­i­nal or even com­plete­ly replac­ing it. The library writer will not know that this hap­pened and the con­tracts could be bro­ken. So even if the library writer has done every­thing right, it can still fail in weird ways.

Now there are way to mit­i­gate these prob­lems, you can hide inter­nal func­tion and vari­ables and only call the exter­nal inter­face for noti­fi­ca­tion style calls where you don’t care what they do with the val­ues but these sys­tems com­pli­cate the code and only to over­come a short­com­ing of the language.

The new­er specs are adding fea­tures to help with this but they aren’t wide­ly avail­able nor used yet. I will talk about these in a future post. It would be nice if we could start over and move to some­thing like Dart but unfor­tu­nate­ly we have too much lega­cy code to be able to change now.

Leave a Reply