The case of the defined undefined property
I like JavaScript, for some reason, I really do, and I still write and maintain a few open source JavaScript projects. It’s undeniable that it has bad parts though, that remain today, even in strict ES2017. In this post, I want to show you one that builds an interesting bug farm.
Being a dynamic language, JavaScript allows you to add properties to pretty much any object, on the fly. This can be done using the dot notation (a.b = 1), or the indexer notation (a['b'] = 1). The indexer notation is more verbose, but it also allows for more flexible scenarios. For example, you can use the value of a variable or function parameter to set a value on an object:
That looks innocuous, but what would happen if we put JavaScript’s careless tendency to convert anything on-the-fly into the mix? What would happen if we used an undefined variable as the index? Let’s find out.
Ouch! That looks bad. So what happened here? Well, the value of b here is undefined, but JavaScript needs a number or a string in this context, so it converts your undefined value to the string ‘undefined’ and then uses that as the index value. When we try to retrieve the value of the property indexed by c, the same conversion happens, and we retrieve the same value.
Seems far-fetched? Check this one out:
Notice the spelling on lines 15 and 16. This is a much more likely case where a typo in the program goes undetected and caused unexpected results. The consequences can be very serious, depending on the context. In normal conditions, everything seems to work fine. You set a property, retrieve it, and the value is the one you set. This is an illusion however, because you’re creating that bucket of undefined properties that all leak one another’s value. You might set a value for one user, for example, in some cache in a Node application, only to have it propagate to another user. That is bad™.
Any bug is bad, but a bug that can go undetected until it causes mayhem is worse. So what can you do to detect something like this? Well, maybe JavaScript’s strict mode should error on implicit conversion of undefined to string: I can’t think of a situation where you would intentionally do that, and even if there’s one, being explicit about it would be better. Since it doesn’t, however, one thing we can do is build a little trap.
Here’s a bookmarklet that you can bookmark to set-up the trap (keep in mind your browser will likely not let this run just anywhere, like on https, but you can always run the same code in the F12 console):
Here's the source code for it:
Once you’ve executed the bookmarklet by clicking on the bookmark, the active web page in your browser is now instrumented so that if any object property is set with undefined (or the string ‘undefined’), the browser will break execution into the debugger. Go up one level in the stack trace, and you’ll see the responsible code. It may not be the ultimate answer to this problem, but it may help find a few hidden and nasty issues in your client-side code.