User-defined types and hoisting in JavaScript

Ben Aston
5 min readApr 7, 2015

--

This is part three of a series starting with A brief history of JavaScript.

In my previous post we learned about value and reference types in JavaScript; how objects and value types have different semantics.

We also saw that “everything is an object”. Even value types are boxed into objects when treated like them. This is known as auto-boxing.

Finally, we started to look at scope and we saw how functions define scope.

Here, we will look at the definition of user-defined types and hoisting.

Functions are heavily overloaded in JavaScript. They are used for scoping, for defining user-defined types and for functions and methods. Methods are simply functions that are invoked on object instances.

We declare a user-defined type as follows:

function Car() {
this.color = 'blue';
}

This is akin to defining a Car type in Java or C#. We instantiate an instance using much the same syntax:

var myCar = new Car();

We call functions like our Car function “constructor functions”. They are factories for object instances in much the same way classes are in other languages. The convention is to name constructor functions with a capital letter.

var myCar1 = new Car();
var myCar2 = new Car();
myCar1 === myCar2; // false

We can use constructor parameters like so:

function Car(color) {
this.color = color;
}

We can use a similar syntax for adding methods:

function Car(color) {
this.color = color;
this.isDriving = function() {
/* some code */
};
}

There is an alternate, syntax too:

function Car(color) {
this.color = color;
}
Car.prototype.isDriving = function() {
/* some code */
};

This syntax is more common because it offers an advantage in terms of memory consumption.

Finally, EcmaScript 6 introduces syntactic sugar in the form of classes, although under the hood, the behavior remains unchanged from previous versions of JavaScript.

In ES6 it might look something like:

class Car {
constructor(color) {
this.color = color;
}
isDriving() {
/* some code */
}
}

Hoisting

Hoisting is an important behavior of variables and functions in JavaScript. It means that variables and functions are conceptually defined at the top of the enclosing function. Functions are hoisted higher than variables.

For example:

function foo() {
bar();
var x = 1; // ...but this declaration is hoisted...
}

This is equivalent to:

function foo() {
var x;
bar();
x = 1;
}

So this means that variables are frequently declared at the top of functions by JavaScript developers, because this is where they will be hoisted anyway.

Functions are also hoisted.

function foo() {
var x;
function bar() {}
}

Here, function bar is hoisted to the top of function foo, above even the declaration of x.

If a function and a variable of the same name are defined in the same scope then the function will win, because it will be hoisted higher than the variable.

function foo() {
var x;
function x() {}
console.log(typeof x);
};
foo(); // function

Note however, that if a variable is initialized in your function, then this will change things, because the initialization will occur after the hoisting, overwriting any function declaration of the same name.

function foo() {
var x;
function x() {}
x = 1;
console.log(typeof x);
};
foo(); // number

Note that EcmaScript 6 introduces the let keyword for block-scoped variable definitions. Variables declared using let are not hoisted.

Program Evaluation

When a JavaScript is loaded into the runtime, functions and variables in the global execution context are evaluated. This means that the global scope is initialised with the hoisted functions and variables.

When a function is invoked, an ExecutionContext is created as a container for the metadata associated with the function call. Active ExecutionContexts form a stack, with the top one being the running ExecutionContext.

The most important parts of the ExecutionContext are the LexicalEnvironment and the this binding.

The LexicalEnvironment, together with its [[Scope]] property form the chain of LexicalEnvironments that make the “scope chain” (and enable closures).

Diagram representing the ExecutionContext and LexicalEnvironment for a simple piece of JavaScript.

Each LexicalEnvironment consists of a collection of EnvironmentRecords, each mapping a variable or function name to its associated value or memory address.

Summary

Functions are heavily overloaded in JavaScript, providing scope, abstract data types and plain old functions and methods. Functions can be used as object factories akin to classes in other languages, when combined with the new keyword. Methods and other properties can be added to the resulting object directly, or to the prototype of the constructor function. The latter approach can save memory.

EcmaScript 6 introduces a short-hand for defining your own types, using the class keyword.

Variable declarations (not initializations) and function declarations are hoisted to the top of the enclosing scope. This can lead to counter-intuitive behavior. Declaring variables at the top of the scope can help improve the legibility of code. ES6 introduces block scope that mitigates many of these problems.

JavaScript source code is interpreted in a well-defined manner. Each function invocation results in the creation of an ExecutionContext that contains metadata about the function invocation, including a link to the outer scope and information about the this binding.

This series continues with Objects, properties and methods in JavaScript.

My name is Ben Aston and thank you for reading my post today. I am a London-based JavaScript consultant, available for short-term consultancy globally. I can be contacted at ben@bj.ma.

If you would like to further develop your knowledge of JavaScript you might be interested in my hands-on training course. Full details can be found online at www.learnjavascript.london.

If you enjoyed this piece, please let me know by clicking the “recommend” button below.

You may also be interested in my post on closures in JavaScript.

Made in the United Kingdom

--

--