Forgetting to declare variables

Here we have a variable, which we've named message, it holds the text 'hello world', and it was created using the var keyword:

var message = "hello world”;

So we've defined a variable exactly as we should. But what happens if we forget the var keyword and only provide the variable name? What will happen?

message = "hello world”;

If we run our code, we don't see an error, so this code ran successfully. And therefore a value called message was created, but was it as a variable?

Problems with the global object

In fact, it was added to something called the global object. The global object is something that can be accessed anywhere within our application. And this behavior can be very helpful. We're going to be working with the global object a great deal in this course and in our applications in general because it gives us a lot of useful features wherever we need them.

And note that the global object is different across environments where JS is run. We're running JS in the browser right now, and for the browser, the global object is named window, and for JavaScript running on the server, it is called global. To resolve these different names, and just use a single reference to the global object, a brand feature is being added to the language in 2020, that enables us to use the name globalThis to properly reference the global object across any environments.

If our environment is a browser, you can refer to the global object with window. But note that, in the future, if you're writing JS in multiple environments, it’s better to use globalThis instead.

So here, in the browser, can see our global object, window, if we just console.log(window):

console.log(window); // Window

So to see that the message was put on the window, we can say:

console.log(window.message); // "hello world"

And we get the text message was set to, hello world.

Note that when working with the global object, we often don't have to reference it by name.

So we can omit window entirely...

console.log(message); // "hello world"

And we still get message's value.

So this is the important thing if we forget the var keyword when creating a variable--even though we can access it like it's a variable it isn't not a variable. It's a property of the global object.

And this should be concerning to you as a JavaScript developer, because if we have access to values from the window without knowing it, what if there is a local variable with the same name? For example, we could have a valid variable named message:

message = "hello world";
var message = "hello again";

How do you know which value you're using: the property or variable?

The problems are even more apparent if we use a name that already exists on the global object. For example, a function that we can use throughout our program to display other developers of something is the console.log function. We can pass it any message we like:

console.log("hi");

And console, what that we just used, is also a property of the window, we're just omitting the window reference. So what if we incorrectly create a variable with the same name, console, but try using console.log, the function?

console = "hi";
console.log("hi"); // Error: console.log isn't a function

Think about what we've seen earlier with variables declared without var and try to guess what will happen when our code runs...

We get an error telling us console.log isn't a function. Why? Because we just overwrote the console property on the window object, simply because we forgot var.

Strict mode

How can we fix this behavior?

First, let’s take a step back and talk about how JS is run. There are two ways or modes that JavaScript can be executed, one called sloppy mode and another called strict mode:

  • Normal “sloppy” mode is the default in scripts
  • On the other hand, strict mode is a mode where several pitfalls of normal mode are removed and more exceptions are thrown and can be switched on in scripts.

Using strict mode

Since we're in sloppy mode right now, let's try strict.

You can switch on strict mode for a complete file, by putting the following code in the first line. Use strict written exactly like this as a string:

"use strict";

If we try running our previous code in strict mode, when you try to use a variable that hasn’t been created:

"use strict";
message = "hello world"; // Reference Error

...we get a Reference error that stops the program, telling us the variable we're trying to use isn't defined. And this is exactly the behavior we want. We want this error to stop our program to prevent mutating the global object and potentially overwriting existing properties on it.

use strict is helpful, but doesn’t fix everything

There is a lot of things that use strict does beyond this and for such helpful errors, we want to run our JavaScript in strict mode whenever we can.

So know that strict mode helps with variables and a lot more quirks of the language, but there are still some issues when working with var that to be aware of, like hoisting:

Hoisting

What is hoisting? Let's take another example. Say we have a valid variable, age, initialized to 26:

var age = 26;

So let's look at what age contains by logging it to the console:

var age = 26;
console.log(age); // 26

Works like we would expect, but what if we tried to log age or access it before it was created? What will happen?

console.log(age); // undefined
var age = 26;

We don't get 26, as before, but we get the empty value of a variable, undefined. So what's happening?

Hoisting is the way JS developers describe the strange ability to access a variable before it's been created. When the JS engine, the thing that runs our code, looks at our code, it's as if the variable's declaration has been hoisted or lifted up to the top of the program, and nothing else, not the value the variable is set to. To rewrite our program in terms of what the engine sees, it looks like this:

var age;
console.log(age); // undefined
age = 26;

...where the declaration is brought to the top or hoisted to the top of the context its running it, which is known as its scope.

That's why we get the default, empty value of the variable, undefined when we console.log it.

This doesn't make any sense to be able to use something before it's been made, so we would hope that this causes an error in strict mode, but it doesn't.

So if strict mode fixes some of the problems with var, but not all of them, including the problem of hoisting, what can we do?

We'll cover that in the next section...