4

The biggest issue I run into in JavaScript is silent typos. For example, the other day I had a variable inIframe. Somewhere else I typed isIFrame. 10-15 mins of debugging later I found it and changed it to inIFrame. It was yet another 5+ mins until I figured out it was still wrong. Should have been lowercase f.

That got me wondering. What would happen if there qas the option to make undefined not convert to any other type? So for example

var a;
var b = a + 1;  // currently will become NaN.

What if I could go

"using strict undefined";
var a;
var b = a + 1;  // throws because undefine does not convert to number

Many things would still work

function foo(options) {
  var value = options.value || 27; // the default is 27?

Or maybe that's a conversion to bool and would be disallowed? You'd have to do

function foo(options) {
  var value = options.value !== undefined ? options.value : 27;

I love that JavaScript is free-form but I hate every time I spent 15-30 minutes tracking down typos.

Are there things that would just totally no longer work that I'm missing and that would now be super cumbersome? Of course like shown above it would be opt-in just like "use strict" is now, so using it wouldn't break existing code.

Maybe this particular idea wouldn't solve that many issues. Is there some way to keep most of the flexibility but lower the chances for errors? Maybe something that could be proposed to standards bodies?


To be clearer as an opt in feature the suggestion wouldn't break any existing code. The question is rather would it break the language itself. Would it make some part of the language impossible to use. A quick glance seems like no it wouldn't. Certain things you'd have to do differently. If undefined does not convert then

"use strict undefined";
var obj = {};
console.log(obj.c);  // Throws, can't convert undefined to string

would throw. But you could easily write

console.log(JSON.stringify(obj)); 

If want to see all of it or

console.log((obj.c === undefined ? "not defined" : obj.c);

or even

function unToStr(a) {
   return a === undefined "**not defined**" : a;
}

console.log(unToStr(obj.c));

I'm not really concerned with that because that's not breaking the language. It's just requiring a little more care (about the same amount of care as say python)

I like JavaScript quite a bit but I hate when I spent 45 minutes tracking down a math bug only to find that a tiny typo has introduced NaN somewhere.

Another way to phrase this question is could this feature be introduced in ES8 or is there something about it that's fundamentally incompatible with JavaScript as a language?

1 Answer 1

2

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 theif/else. The next step is to run a debugger and check for the actual values: as soon as you see theundefined` 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.

10
  • +1 for static checkers and linters, -1 for auto-completion. Auto complete is good for convenience/speed, but if you start to rely on auto-completion to make sure you type things correctly, you will start having to use auto-completion all the time, even when it's not convenient. Lint is the right answer.
    – Brandin
    Commented Nov 11, 2015 at 11:24
  • 1
    The linter will point out no such thing. It has no idea what properties exist in a object and which ones don't as properties are added at runtime not compile time. I can just as easily do s["in"+"Iframe"] = true; The linter will have no way to know at linting time whether or not s.inIframe is valid or s.isIframe is valid. In fact your own example shows the issue var obj = JSON.parse("{\"c\":123}"); As for breaking stuff "use strict" already breaks stuff. That's why it's opt in. I was also suggesting an opt in feature. So it wouldn't break anything because you'd be opting in.
    – gman
    Commented Nov 11, 2015 at 12:18
  • @Brandin: good point. I edited my answer to emphasize the importance to use a linter, instead of relying purely on auto-completion. Commented Nov 11, 2015 at 14:09
  • @gman: the linter will point out the error in your code, as I explained in my answer. Your s["in"+"Iframe"] example is irrelevant, since you're simply screwing up with linter/stricter interpreter. It's exactly the same thing that using Reflection in languages such as Java or C# and blaming the compiler that it couldn't tell you that typeof(Product).GetMethod("FindPrice").Invoke(...) will throw an exception because Product doesn't contain FindPrice method. Commented Nov 11, 2015 at 14:15
  • Pasted this into jslint. Checked "tolerate this", didn't get any errors.
    – gman
    Commented Nov 11, 2015 at 15:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.