Daily Tips & Tricks for Web Enthusiasts

JavaScript

Computers: Almost Always Great at Math

One of the reasons I love computers so much is because I hate math. Math and I do not get along, so having a magic device that can do most any math problem I throw at it instantly and accurately has a great deal of appeal for me.

Except, well, sometimes that accuracy piece isn’t all it’s cracked up to be.

Let’s take a look at an extremely simple bit of code:

var result = 0.1 + 0.2;

After running this code you’d expect the result variable to contain the value 0.3, but that’s not what happens. When this code is run the value stored in the result variable will, in fact, be 0.30000000000000004.

I know what you’re thinking, but no, this isn’t another strange JavaScript thing. This is, in fact, a strange computer thing. If you were to run similar code in languages like C or Java you would get the same result.

The reason for this behavior has to do with how computers translate back and forth between the base 10 numbering system we humans are used to and the base 2 numbering system they use internally. If you’re curious about it check out the aptly named 0.30000000000000004.com website for the technical details (and links to even more technical details).

Suffice it to say, you should be careful when doing floating point math. Run some tests and make sure you’re getting the results you expect. If you’re not, make changes to how you do math based on what you now know about how computers handle floating point numbers.

Don’t Write Magic, Hard-to-Understand Code

Take a look at this:

var string = '42';
var foo = +string;

Do you know what that second line does? Do you know what foo will contain when this code is run? Chances are you don’t, even if you’re a seasoned JavaScript developer.

This is an example of what many refer to as “a neat hack” or “a bit of magic.” I, on the other hand, refer to this as “hard to read and understand.”

That second line above takes advantage of how the + operator works in JavaScript to cast the content of the string variable to a number. Thus, foo now contains the number 42 instead of the string '42'.

Still find that code hard to understand even after I explained it? I don’t blame you.

Compare the code above to the following:

var string = '42';
var foo = Number(string);

This code does the exact same thing, but it omits confusion and embraces clarity. It doesn’t depend on the reader having obscure knowledge of the esoteric behaviors of JavaScript. It’s readable and understandable.

Here’s another example:

var string = 'hello';
var foo = !!string;

What? What’s foo now?

This code casts the value of the string variable as a boolean and sets foo to true. I’ve been using JavaScript for years and years, but if I came across this code I’d have no idea what it did without looking it up. This technique might be a fun intellectual curiosity, but it should never be used in practice.

Examine this alternative:

var string = 'hello';
var foo = string ? true : false;

This reads almost like a sentence. If string is truthy, set foo to true, otherwise set foo to false. Easy to read, easy to understand; the hallmark of great code.

It can be tempting to show off a neat trick you just learned, or show how deep your knowledge of a programming language goes. Doing that, however, is just going to come back to haunt you. Future You is unlikely to remember this stuff unless you use it constantly, and the vast majority of other developers won’t appreciate it. Most will see an annoying hurdle standing between them and understanding the code, not a nifty trick that will save them a few keystrokes.

Avoid temptation. Write code that’s easy to read and easy to understand.

How to Avoid Confusing Code Hoisting Surprises

JavaScript does some things behind the scenes that you may not expect. One of these is called code hoisting, and it can cause some strange issues if you don’t know what’s going on behind the scenes.

The way code hoisting works applies to functions and variables a bit differently, so we’re going to examine each one individually.

Function Hoisting

Let’s start with this example:

function sayHello() {
    console.log('Hello!');
}

sayHello(); // Hello!

That’s pretty straightforward. Running this will log Hello! to the console.

Let’s take a look at another version:

sayHello(); // Hello!

function sayHello() {
    console.log('Hello!');
}

This code works in exactly the same way as the previous example, but how? We called the sayHello() function before we declared it, so why does this code work?

The answer is that the function declaration was hoisted to the top, effectively making both of these examples work as if they were written like the first example.

Whenever you declare and initialize a function with the function keyword, that function is hoisted to the top of the scope where you declared it at runtime. This allows you to call the function before declaring it.

Here’s a longer example that will make this clearer. Examine this code:

piggyOne();

function piggyOne() {
    console.log('Straw!');
}

piggyThree();

function piggyTwo() {
    console.log('Wood!');
}

piggyTwo();

function piggyThree() {
    console.log('Stone!');
}

When this code is run the function declarations will be hoisted, which effectively makes the code run as if it was written like this:

function piggyOne() {
    console.log('Straw!');
}

function piggyTwo() {
    console.log('Wood!');
}

function piggyThree() {
    console.log('Stone!');
}

piggyOne();

piggyThree();

piggyTwo();

That’s essentially how function hoisting works.

Variable Hoisting

Before we dive in to how variables are hoisted it’s important to understand the difference between declaring a variable and initializing a variable.

var theAnswer; // Declaration
theAnswer = 42; // Initialization

Declaring a variable is like saying, “this variable exists,” whereas initialization is like saying, “here’s what this variable contains.”

You can also declare and initialize a variable at the same time:

var theAnswer = 42; // Declaration & Initialization

Now that you know the difference between declaring and initializing a variable, let’s get back to code hoisting by explaining that JavaScript hoists variable declarations, but it does not hoist variable initializations.

Let’s look at some examples, starting with this code, which will throw an error:

console.log(theAnswer); // ReferenceError: theAnswer is not defined

We didn’t define theAnswer, so that error makes sense. Now let’s both define and initialize theAnswer before trying to log it:

var theAnswer = 42;

console.log(theAnswer); // 42

This example logs 42 to the console, as expected.

Now take a look at this:

console.log(theAnswer); // undefined

var theAnswer = 42;

This code logs undefined to the console, which is an interesting outcome.

Remember, JavaScript only hoists variable declarations, not initializations. That means the code above effectively executes like this at runtime:

var theAnswer; // Hoisted!

console.log(theAnswer); // undefined

theAnswer = 42;

The declaration of the variable was hoisted, but the initialization was not. JavaScript is essentially tearing the single line of code that declared and initialized theAnswer in half, and putting the declaration at the top. This prevents the console.log() line from throwing an error, but it because theAnswer hasn’t been initialized yet, the value inside is undefined.

What About Variables Containing Functions?

You might be wondering how code hoisting works in a scenario like this:

sayHello();

var sayHello = function() {
    console.log('Hello!');
}

This code will actually throw an error: TypeError: sayHello is not a function. That’s because, in this case, sayHello is being declared as a variable with the var keyword, so only that variable declaration is hoisted to the top, effectively making the code run like this:

var sayHello; // Hoisted!

sayHello(); // TypeError: sayHello is not a function (execution stops here)

sayHello = function() {
    console.log('Hello!');
}

One thing to note is that JavaScript isn’t actually rewriting your code when it hoists function and variable declarations to the top. What’s going on behind the scenes is a bit complicated, and has to do with JavaScript putting function and variable declarations in memory during the compile phase. The good news is that you don’t need to know the technical specifics of code hoisting to understand how it works and the impact it has on your code.

Now that you know how code hoisting works I recommend you work with it instead of against it. That is, declare your functions and variables first. JavaScript is going to do so anyway, and if your code matches what’s actually going on behind the scenes it will be easier to manage and debug, with fewer surprises.

Default Values for Arguments

If you don’t need to support Internet Explorer, all of the other major browsers allow you to specify default arguments in your JavaScript functions like this:

function foo(argument = 'default') {
    // ...
}

With this code, if foo() is called and argument is undefined (either because the argument is missing or because it is explicitly set to undefined), the default value of 'default' will be assigned to argument.

Now, if you do need to support older browsers like IE, you may find yourself writing something like this:

function foo(argument) {
    if (!argument) {
        argument = 'default';
    }

    // ...
}

That works, but this approach will get out of hand pretty quickly as the number of arguments increases. I’ve got good news, though! There’s a better way:

function foo(argument) {
    argument = argument || 'default';

    // ...
}

These last two code snippets function in exactly the same way, but the second version is shorter and easier to maintain.

This version works because the || operator (which is usually used in if statements) will first check to see if what’s to the left can be converted to true and, if it can, will return whatever’s on the left. Otherwise the value on the right is returned. This behavior is called short circuiting, and you can read more about it on MDN if you’re curious about the specifics.

This version also has the advantage of being concise without being hard to understand. You can read this as, “Make the argument variable equal to the original argument value if it exists, or use the default value otherwise.”

One word of warning: This second method doesn’t behave exactly like the native version described at the beginning of this tip. The native version of default argument value assignment checks to see if argument values are undefined or not, but the second method checks if they convert to true or not. That means values that convert to false, like an empty string ('') or a zero (0), will trigger the default value.

In other words, calling foo(0) using the first code snippet above would assign 0 to argument inside the function, whereas calling foo(0) using the second or third code snippets would assign 'default' to argument instead (because 0 converts to false and, thus, triggers the default value assignment).

Access Object Properties with Variables

Typically object properties in JavaScript are accessed using the dot notation, like this:

var object = {
    foo: 'bar'
};

var example = object.foo; // example is now 'bar'

But what if you don’t know the property name at runtime?

Let’s say you wanted to write a function called propertyOfObject() that took two arguments, object and propertyName, and returned the value of the specified property. Dot notation won’t work in a situation like this:

function propertyOfObject(object, propertyName) {
    return object.propertyName; // This doesn't work!
}

That would look for a property that was literally named propertyName, which is not what we want.

The solution is to use bracket notation to access the property value, like so:

function propertyOfObject(object, propertyName) {
    return object[propertyName]; // Bingo.
}

Any object property can be accessed using bracket notation, and you can put any expression you want inside the brackets, as long as that expression evaluates to a string for the property you’re after. All of the following are valid approaches:

object['foo'];
object[propertyName];
object[someFunctionThatReturnsAPropertyName()];

Now you can access object properties at runtime without knowing their names in advance.

Be Careful with Code Formatting

Usually code formatting is a matter of personal preference, with no impact on how code actually works, but there are exceptions.

Let’s start with this if statement:

if (foo) {
    // Do something if foo is true.
} else {
    // Do something else if foo is false.
}

Some people prefer to format their code differently, and would write the if statement above like this:

if (foo)
{
    // Do something if foo is true.
}
else
{
    // Do something else if foo is false.
}

So far, so good. This code looks different, but it works exactly the same.

Now let’s look at a function that returns an object:

function foo() {
    // Imagine some code here.

    return {
        property1: value1,
        property2: value2
    };
}

Good times, no problems here. But what if we try to format this differently, like before?

function foo()
{
    // Imagine some code here.

    return
    {
        property1: value1,
        property2: value2
    };
}

Everything might look fine, but making these formatting changes actually broke the function! Depending on where and how this code is run the foo() function will either return undefined or the code will refuse to run at all and throw a syntax error.

Why? It has to do with semicolons!

Yes, you heard me correctly: semicolons! As you may know, semicolons in JavaScript are (mostly) optional. The reason they’re optional is that JavaScript parsers have a step called automatic semicolon insertion (ASI) which inserts semicolons for you. The problem with the function as written above is, after the parser adds semicolons, you get this:

return;
{
    property1: value1,
    property2: value2
};

So now you’ve got a return statement with nothing attached, meaning one of two things will happen:

  1. The function will return undefined when it hits the return statement and the code following it will never be run.
  2. The parser will throw a syntax error when parsing the code because there’s an object hanging out down there doing nothing, and will refuse to even run the code in the first place.

This also applies to arrays. This is okay:

return [
    value1,
    value2
];

But this is not:

return
[
    value1,
    value2
];

So, regardless of how you format your code, watch out for situations like this. Generally speaking, you’re fine formatting blocks and scopes however you wish; however, object and array literals may need to be treated differently depending on your preferred style.

Naming Functions & Keeping Them Simple

Every time I write a function or method, I take a moment to describe it to myself in plain English. Doing so serves two purposes: It helps me determine if the function is too complex, and it helps me come up with a good name for the function.

If the description I come up with for a function is more than a single sentence, or if that single sentence contains the word “and”, that indicates the function is too complex. In order to keep code understandable and maintainable, each function should serve a single purpose. There are, of course, exceptions to this rule, but they’re few and far between.

If a function does more than one thing it’s likely I’ll end up needing to do those individual things separately at some point in the future, so it’s a good idea to stop and break the function up into individual pieces now before things get too out of hand. It’s also much easier to understand and maintain code that’s broken up into small, easy-to-digest chunks with few (if any) dependencies on each other.

Once I have a function that produces a proper single-sentence, single-purpose description it’s usually easy to turn that sentence into a good, descriptive name for the function. Reduced code complexity and great function and method names with one simple technique!

So, how would you describe your functions?