There are two possible cases where such typo could be an issue:
Names of variables
Such as in:
var inIframe = ...
...
console.log(isIFrame); // Throws `ReferenceError: isIFrame is not defined`
The fact that you discover the mistake at run time can eventually be disturbing. The case is straightforward if the declaration of a variable and the use of it wrongly typed are close to each other, but could be much more problematic if the typo is buried inside conditional statements, loops and functions.
The fact is that in JavaScript, it's perfectly legal to use variables without declaring them first:
a = 5; // See, no `var` here.
console.log(a);
But the fact that the current JavaScript interpreter doesn't check that a variable was declared before being used really doesn't matter that much: such typos are very easy to avoid by using linters.
JavaScript has a great linter tool jslint written by Douglas Crockford. The following code:
/*jslint browser: true, devel: true*/
var a = function () {
'use strict';
var b = 123;
console.log(b);
};
produces no errors, but if you change:
console.log(b);
to:
console.log(c);
you'll see the following errors:
Unused 'b'.
Undeclared 'c'.
making it obvious to figure out the typo.
In your case, jslint will immediately figure out that isIFrame
is unused and point you out to it. Obviously, it won't be able to understand that you wanted to write inIframe
, but at least, you won't waste twenty minutes debugging your code.
See also:
An answer, which, among others, compares jsLint and jsHint and explains why jsHint should be avoided.
My article explaining why is it crucial to use linters and static checkers.
Names of properties
Such as in:
var something = {
"inIframe": true
};
...
console.log(something.isIFrame); // Shows `undefined`
The dynamic nature of JavaScript prevents both the interpreter and the linter from determining that something.isIFrame
doesn't point to anything. In fact, there could be an isIFrame
inside, coming from multiple sources, like prototypes or direct assignment. Also, something
can be initialized like this:
var something = JSON.parse('{"inIframe": true}');
where the actual string could be a result of a REST call, meaning that the interpreter (or the linter) couldn't possibly know what's inside. Google Closure Compiler may help here, but at a cost of requiring you to manually describe the structure of something
, similarly to what one would do in a static strongly-typed programming language.
The next thing is to determine why is JavaScript actually returning undefined
instead of throwing ReferenceError
(or a similar exception) at run time when encountering an access to a property which doesn't exist.
Technically, until the apparition of proxy in ECMAScript6, nothing prevents to actually throw an exception, since, I imagine, the JavaScript engine knows that a given property doesn't exist. It should also be a possibility for the engine to make a difference between getting the property and setting it (like it's already done for variables; a = 123;
is legal, but console.log(b);
throws an exception).
On the other hand, the choice of actually returning undefined
makes sense too, for two reasons:
If you expect isIFrame
to be either false
or true
, receiving undefined
gives you a good hint that there is something wrong with the name of the property. The same applies to most cases where you expect a value, would it be a number, a string, or an object representing some business entity.
The case which worries me much more is when, expecting a boolean, you write:
if (something.isIFrame) {
// This will never execute, since `isIFrame` doesn't exist.
} else {
// This will execute every time.
}
Here, JavaScript doesn't help you much, since it silently swallows the undefined
and simply executes the elseblock every time. This being said, it shouldn't take much time to figure out that there is something wrong around the
if/else. The next step is to run a debugger and check for the actual values: as soon as you see the
undefined` instead of a boolean, you have a hint.
You may on the other hand expect the situation where the value may simply not be here.
For instance, I'm currently working on a node.js application which receives messages about servers. Sometimes, a message is sent not by the server itself (a virtual machine), but by the host: in this case, the message contains a replacementIP
property with the IP address of the concerned virtual machine.
If I want to display the IP address, all I have to do is:
var ipToDisplay = message.replacementIP || context.senderIP;
If JavaScript was throwing exceptions when accessing non-existent property, this would force me to write this instead:
var getActualIP = function (message, context) {
try {
return message.replacementIP;
} catch {
return context.senderIP;
}
};
[...]
var ipToDisplay = getActualIP(message, context);
I find it much more difficult to follow.
So you have two cases here: in the first one, the choice of the designers of JavaScript appears problematic in a specific case, and causes you to launch your debugger. Not that nice, indeed. On the other hand, the second case shows that the designers' choice makes it possible to write very compact code in a lot of situations; I find it very nice.
So what about an opt-in "using strict undefined"
?
Not sure it's a good idea, for the simple reason that adding features to a language should be taken very, very seriously.
Any additional feature takes literally months, and given the nature of JavaScript, would need to be implemented in every browser. As usual, the one with the name starting by “I” will be late, and so you'll find yourself including "using strict undefined"
in your code, while knowing that this browser will plainly ignore it: not fun when it comes to debugging.
Compare this to, for instance, ECMAScript 6th String Interpolation feature. String interpolation brings a huge benefit in terms of code readability and maintainability: most developers who used Python or Java are familiar with this feature, and they will happily use it in JavaScript. It will break existent code, but given its importance, it's worth the risk, and worth the time spent implementing it.
Default parameter values? Same thing: great benefit for everyone, and no risk of breaking existent code.
But "using strict undefined"
? I'm not convinced that the edge case illustrated below is not that important, especially since it's easy to find what's happening with the help of a debugger. If you write software for which it is not acceptable to access non-existent properties, Google Closure Compiler is an option. Switching to a different language is another option.