Best practices

Strict Mode - Robust Client-Side JavaScript

ECMAScript 5 (2009) started to deprecate error-prone programming practices. But it could not just change code semantics from one day to the next. This would have broken most existing code.

In order to maintain backwards compatibility, ECMAScript 5 introduces the Strict Mode as an opt-in feature. In Strict Mode, common pitfalls are removed from the language or throw visible exceptions. Previously, several programming mistakes and bogus code were ignored silently. The Strict Mode turns these mistakes into visible errors – see failing fast.

Enable the Strict Mode by placing a marker at the beginning of a script:

Code language: JavaScript

'use strict';
window.alert('This code is evaluated in Strict Mode! Be careful!');

Or at the beginning of a function:

Code language: JavaScript

function strictFunction() {
  'use strict';
  window.alert('This function is evaluated in Strict Mode! Be careful!');
}

Syntax-wise, 'use strict'; is simply an expression statement with a string literal. This code does not do anything when evaluated. It is a meaningful marker for browsers that support ECMAScript 5, and innocuous code for browsers that do not.

Quoted content by Mathias Schäfer is licensed under CC BY-SA. See the other snippets from Robust Client-Side JavaScript.

Duck typing - Robust Client-Side JavaScript

As a weakly typed language, JavaScript performs implicit type conversion so developers do not need to think much about types. The concept behind this is called duck typing: “If it walks like a duck and quacks like a duck, it is a duck.”

typeof and instanceof check what a value is and where it comes from. As we have seen, both operators have serious limitations.

In contrast, duck typing checks what a value does and provides. After all, you are not interested in the type of a value, you are interested in what you can do with the value.

[…]

Duck typing would ask instead: What does the function do with the value? Then check whether the value fulfills the needs, and be done with it.

[…]

This check is not as strict as instanceof, and that is an advantage. A function that does not assert types but object capabilities is more flexible.

For example, JavaScript has several types that do not inherit from Array.prototype but walk and talk like arrays: Arguments, HTMLCollection and NodeList. A function that uses duck typing is able to support all array-like types.

Quoted content by Mathias Schäfer is licensed under CC BY-SA. See the other snippets from Robust Client-Side JavaScript.

Conditional statements and truthy values - Robust Client-Side JavaScript

The key to robust JavaScript is asking “if” a lot. During the concept phase, ask “what if”. In the code, ask if to handle different cases differently.

The if statement, or conditional statement, consists of a condition, a code block and an optional second code block.

Code language: JavaScript

if (condition) {
  // …
} else {
  // …
}

When an if statement is evaluated, first the condition expression is evaluated. The result of the expression is then converted into a boolean value, true or false. If this result is true, the first code block is executed, otherwise the second block, if given.

Most likely, this is not new to you. The reason we are revisiting it is the conversion into boolean. It means you can use a condition expression that does not necessarily evaluate to a boolean value. Other types, like Undefined, Null, String or Object are possible. For example, it is possible to write if ("Hello!") {…}.

If you rely on the implicit conversion, you should learn the conversion rules. ECMAScript defines an internal function ToBoolean for this purpose. In our code, we can use the public Boolean() function to convert a value into boolean. This delegates to the internal ToBoolean function.

To illustrate the conversion, imagine that

Code language: JavaScript

if (condition) {
  // …
} else {
  // …
}

is a short version of

Code language: JavaScript

if (Boolean(condition) === true) {
  // …
} else {
  // …
}

Values are called truthy when ToBoolean converts them into true. Values are called falsy when ToBoolean converts them into false.

The way ToBoolean works is simple, but with a twist. Let us quote the ECMAScript specification which is quite readable for once:

ToBoolean Conversions
Argument Type Result
Undefined Return false.
Null Return false.
Boolean Return argument.
Number If argument is +0, -0, or NaN, return false; otherwise return true.
String If argument is the empty String (its length is zero), return false; otherwise return true.
Symbol Return true.
Object Return true.

As you can see, most types have a clear boolean counterpart. All objects, including functions, dates, regular expressions and errors, are truthy. The two types denoting emptiness, undefined and null, are falsy.

For numbers and strings though, it is complicated. Numbers are truthy except for zeros and NaN. Strings are truthy except for empty strings.

Quoted content by Mathias Schäfer is licensed under CC BY-SA. See the other snippets from Robust Client-Side JavaScript.

NaN is contagious - Robust Client-Side JavaScript

NaN is a dangerous beast. NaN is a special value that means “not a number”, but in fact it is a number you can calculate with.

NaN is contagious. All calculations involving NaN fail silently, yielding NaN: 5 + NaN makes NaN, Math.sqrt(NaN) produces NaN. All comparisons with NaN yield false: 5 > NaN is false, 5 < NaN is also false. 5 === NaN is false, NaN === NaN is also false.

If a NaN slips into your logic, it is carried through the rest of the program until the user sees a “NaN” appearing in the interface. It is hard to find the cause of a NaN since the place where it appears can be far from the place that caused it. Typically, the cause of a NaN is an implicit type conversion. My advice is to raise the alarm as soon as you see a NaN.

Quoted content by Mathias Schäfer is licensed under CC BY-SA. See the other snippets from Robust Client-Side JavaScript.

Failing fast - Robust Client-Side JavaScript

Every computer program may have logic bugs: A case is not considered, the state is changed incorrectly, data is transformed wrongly, input is not handled. These bugs can have several consequences in JavaScript:

In the best case the script fails with an exception. You may wonder, why is that the best case? Because an exception is visible and easy to report. The line of code that threw an exception is likely not the root cause, but the cause is somewhere in the call stack. An exception is a good starting point for debugging.

In the worst case the application continues to run despite the error, but some parts of the interface are broken. Sometimes the user gets stuck. Sometimes data gets lost or corrupted permanently.

JavaScript code should fail fast (PDF) to make errors visible. Failing early with an exception, even with a user-facing error, is better than failing silently with undefined, puzzling behavior.

Unfortunately, JavaScript does not follow the principle of failing fast. JavaScript is a weakly typed language that goes great lengths to not fail with an error.

[…]

The key to failing fast is to make your assumptions explicit with assertions.

Quoted content by Mathias Schäfer is licensed under CC BY-SA. See the other snippets from Robust Client-Side JavaScript.

The browser as a runtime environment - Robust Client-Side JavaScript

There are several relevant browsers in numerous versions running on different operating systems on devices with different hardware abilities, internet connectivity, etc. The fact that the web client is not under their control maddens developers from other domains. They see the web as the most hostile software runtime environment. They understand the diversity of web clients as a weakness.

Proponents of the web counter that this heterogeneity and inconsistency is in fact a strength of the web. The web is open, it is everywhere, it has a low access threshold. The web is adaptive and keeps on absorbing new technologies and fields of applications. No other software environment so far has demonstrated this degree of flexibility.

Quoted content by Mathias Schäfer is licensed under CC BY-SA. See the other snippets from Robust Client-Side JavaScript.

Immediately-Invoked Function Expression (IIFE), a.k.a. self-executing anonymous functions

Code language: JavaScript

(function () {
  // Safely declare your names inside this function.
  var variable1 = 1;
  let variable2 = 2;
  const constant = 3;
  function someFunction() {}
  class SomeClass {}
})();

At the heart of this pattern, there is an anonymous function function () {…}. This is a function expression that simply creates a function and returns it as a value, in contrast to a function declaration that creates a function and inevitably binds it to a name.

The braces around the anonymous function, ( function() {…} ), allow the parser to recognize the function expression correctly. Finally, the braces at the end () call the function immediately. That is why it is called immediately-invoked function expression.

Quoted content is licensed under CC BY-SA.

A/B testing can't tell you why a change is better

I think this is a good example of the is-ought problem in philosophy, transplanted into the world of software development:

A/B testing is a great way of finding out what happens when you introduce a change. But it can’t tell you why.

The problem is that, in a data-driven environment, decisions ultimately come down to whether something works or not. But just because something works, doesn’t mean it’s a good thing.

If I were trying to convince you to buy a product, or use a service, one way I could accomplish that would be to literally put a gun to your head. It would work. Except it’s not exactly a good solution, is it? But if we were to judge by the numbers (100% of people threatened with a gun did what we wanted), it would appear to be the right solution.