public

JavaScript hoisting

I often see the same type of problems arise when people try to learn JavaScript. The parts that make this programming language so special are exactly those that are the

10 years ago

Latest Post Deferring decisions in Evolutionary Architecture by Tim Sommer public

I often see the same type of problems arise when people try to learn JavaScript. The parts that make this programming language so special are exactly those that are the root cause of headaches among front-end developers.
How many times in your life have you bartered, plead, cried or prayed for your script to stop complaining and just work?

Please Work JavaScript

This guy gets it: fuckitjs

Many of these problems can be avoided by learning how JavaScript actually works. Simply hacking away just doesn't cut it any more if you want to be a front-end dev.
With this blogpost, I'd like to address the way JavaScript handles 'hoisting'.

What is hoisting?

Hoist, verb, gerund or present participle: 'hoisting'

Raise (something) by means of ropes and pulleys. Synonyms: raise, raise up, lift, lift, haul up, heave up, jack up, hike up, winch up, pull up, heft up, upraise, uplift, elevate

So hoisting is the process of raising stuff..

In JavaScript, variables will be hoisted -read lifted- to the top of their scope, regardless of where they are declared.

However, only the declaration will be hoisted. If the variable is also initialized the value at the top of the scope will initially be set to undefined.

Declaration and initialization?

It is very important to understand the difference between declaring and initializing a variable in JavaScript:

Declaration

//declare variable 'foo' 
var foo; 

Initialization

//and initialized it with value 'bar'
foo = 'bar';

JavaScript hoisting

Let's see it in action:

(function() {
  var a = 'a';
  // lines of code
  var b = 'b';
  // some more lines of code
  var c = 'c';
  // a lot more lines of code
})();

Which is interpreted and executed like this:

(function() {
  var a, b, c; // variables declared
  
  a = 'a';
  // lines of code
  b = 'b'; // initialized
  // some more lines of code
  c = 'c'; // initialized
  // a lot more lines of code
})();

The JavaScript interpreter does this and you have no control over it. You can't even see it happen.

Evil! Why does it do that ?

As Stoyan Stefanov explains in his book "JavaScript Patterns", hoisting is a result of the implementation of the JavaScript interpreter.

For completeness, let’s mention that actually at the implementation level things are a little more complex. There are two stages of the code handling, where variables, function declarations, and formal parameters are created at the first stage, which is the stage of parsing and entering the context. In the second stage, the stage of runtime code execution, function expressions and unqualified identifiers (undeclared variables) are created. But for practical purposes, we can adopt the concept of hoisting, which is actually not defined by ECMAScript standard but is commonly used to describe the behavior.
- Stoyan Stefanov,  "JavaScript Patterns"

Ok, why should I care ?

You should.. Take a look at the following code fragment:

var foo = "bar";
(function () {
    console.log("value is " + foo);
    //outputs 'value is bar'
})();

Nothing special happening here. Now take a look at this:

var foo = "bar";
(function () {
    console.log("Original value was " + foo);
	// Outputs: "Original value was undefined"
    
    var foo = "foobar";

    console.log("New value is " + foo);
    // Outputs: "New value is foobar"
})();

In the second code fragment the original value is undefined. This might seem strange to most c# developers (or inexperienced JavaScript developers), who would mistake the original value for 'bar'.
The JavaScript interpreter, while hoisting the variable declarations, converts the fragment as follows:

var foo = "bar";
(function () {
	var foo;

    console.log("Original value was " + foo);
	// Outputs: "Original value was undefined"
    
    foo = "foobar";

    console.log("New value is " + foo);
    // Outputs: "New value is foobar"
})();

I know, it might seem strange. Bugs can easily be introduced. But, they can just as easily be avoided by declaring your JavaScript variables at the top of their scope. This is a common good practice enforced by all good JavaScript code quality tools.

Function hoisting

Functions are hoisted as well.

isFunctionHoisted();
// Outputs: "Yes!"

function isFunctionHoisted() {
    console.log("Yes!");
}

The JavaScript interpreter allows you to use a function before it is declared in source code. You can thus write high-level code at the beginning of your source file instead of at the end.

However, function definition hoisting only occurs for function declarations, not function expressions.

For example:

isHoisted();
//Outputs: "Function hoisted!"

isNotHoisted();
//TypeError: undefined is not a function

function isHoisted() {
	console.log("Function hoisted!");
}

var isNotHoisted = function () {
	console.log("Function not hoisted!");
};

In the above code sample we see the interaction of two different types of hoisting. Our variable isNotHoisted has its declaration hoisted but not its value, which is undefined. Unnamed function expressions are thus not hoisted.

Same applies when you use a named function expression:

// ReferenceError: funcName is not defined
funcName();
    
// TypeError: undefined is not a function
varName();

var varName = function funcName() {
	console.log("Definition not hoisted!");
};

A function is not hoisted, even if it is part of a named function expression. Therefore, 'funcName' is not defined.

Tim Sommer

Published 10 years ago

Comments?

Leave us your opinion.