Whilst working on my newest project Jolf, I found the need to “edit” the built-ins of JavaScript. What does that mean? I wanted to edit the alert
function so that it would also apply JSON.stringify
on the input. How can I do this? Abusing JavaScript closures, of course!
JavaScript is interesting in that it really doesn't care. For the record. JavaScript doesn't care what you do with it. It grumbles only when it cannot possibly complete the action. Other than arguing with you when you use undefined variables (or make a mistake with variables at all) and when you completely garble the text you type, JavaScript rolls with it. That is why the aim of the post is even feasible.
Now, let's do a simple example. Let's fix the abomination of the isNaN
function. One would expect that this function would return true
if and only the input is NaN
; however, this function tries first to evaluate the input as a number, then detecting if the result is NaN
. So, isNaN(30) => false
, but isNaN("Now in 3d!") => true
. We want it so that isNaN(x) iff x is NaN
. How? Observe that typeof NaN === "number"
. So, we check for a number type first, then call the old isNaN
function, like so:
(function(oldNaNTest){ return isNaN = function(inputNum){ if(typeof inputNum!=="number") return false; // NaN is a number else return oldNaNTest(inputNum); } })(isNaN);
This creates an anonymous function with isNaN
as an argument. And, as an argument, oldNaNTest
is it's own, “separate” function (i.e. being distinct from `isNaN` and not merely a reference), so it retains `isNaN`s value. We do a type-check on the input, and proceed with returning the correct results.
But why an anonymous function? Well, consider what might happen if it wasn't. We would use something like this:
isNaN = function(inputNum){ if(typeof inputNum!=="number") return false; else return isNaN(inputNum); }
All would be well with non-numbers, as they return false
; however, when given a number, we look to the else
statement for our instruction. And what are we pointed to? Yep, isNaN
. Is this wrong? Oh good heavens, yes it is! JavaScript looks at this statement and thinks, now, I need to look at the scope for a function named isNaN
. Does that exist…yes! I found one! Here, take it! JavaScript hands you the function you just made. Why? Because you overwrote the old one, silly. We could create a reference, say, oldNaNTest
, in the global scope, then proceed as normal. Two problems with that: one, you have an extra variable in the global scope. That's dirty! Secondly, if you try to get rid of that variable, you encounter an error, because 'ol JavaScript cannot find the function reference. :( It works, but it is neither elegant nor efficient.
Let's edit the eval
command so that it it only evaluates the argument if it is a valid number. We can even use our new, correct isNaN
function! We'll assume that is appended in the code snippet below.
(function(oldEval){ return eval = function(arg){ // test if valid number var testArg = Number(arg); // will be NaN if invalid return isNaN(testArg)?arg:oldEval(arg); } })(eval);
Simplex enough, eh?