Value types, reference types and scope in JavaScript

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

There are only two things fundamental to JavaScript: objects and functions.

Understand objects and functions and you understand JavaScript.

Functions in JavaScript are objects. Infact, everything in JavaScript is an object. The language does, however, contain specific type optimisations known as value types that have different semantics. This is both for performance reasons, and because people intuitively expect value types to behave in a particular manner.

The value types are number, symbol, boolean, null and undefined. string is also a value type although it is implemented with a slightly different behavior to save memory.

Apart from symbol, these all have a literal syntax. i.e. you can “literally” write them straight into your program.

var a = 1; // the literal value 1 is copied into the memory address labelled ‘a’

Value types have copy semantics.

var a = 1;
var b = a;
a = 2;
b === 1; // true: the value 1 was copied into the memory behind b
Value types have a pass-by-value semantic.

This is exactly the behavior we would expect (and this is why it behaves in this way). Likewise for booleans.

The post-it note drawing here represents the lexical environment present after running the code below it. Here, two variables are added to the lexical environment; a and b. Their values are stored alongside their definition in the lexical environment in a key-value-pair mapping. “Lexical environment” means the context defined by the enclosing scope, in terms of the code as-written.

null and undefined represent empty values. null is intended to represent a user-defined absence of something. undefined is a special value indicating the the absence of anything. null is also used as the top of all object inheritance chains.

Uninitialized variables are undefined.

If a variable is declared and not initialized, it has the value undefined.

Strings are special value types because of the unique memory concerns associated with strings (they can be as long as you want!).

Strings can be thought of as value types, but with the subtle difference that assignment will not copy the entire value. Instead implementations will copy a reference to a single representation of the string maintained internally.

Objects are reference types. Objects have a copy-by-value-of-the-reference semantic.

Objects can be declared in two ways:

var o = new Object();

or

var o = {};

Both of the above are equivalent. The latter is called the object literal syntax.

Objects have a pass-by-value-of-reference semantic.

The copy-by-value-of-the-reference semantic means that references to objects are passed around instead of the objects themselves. This is for performance reasons, in addition to being what most developers would expect.

I mentioned earlier that everything is an object. This is conceptual rule, enabled by a silent feature of JavaScript called auto-boxing.

Auto-boxing is the boxing, or wrapping, of value types in objects when they are treated like objects. Behind the scenes, a temporary object is created for the duration of the use of the value type instance.

For example:

2.0.toString(); // 2.0 is boxed automatically so that the toString method can be invoked.

In this way everything in JavaScript, including value types, can be said to be an object (apart from the types corresponding to the absence of anything).

Some people say that JavaScript is not object oriented. Contrariwise, as we have seen, quite literally everything in the language is an object.

The other important object-category is functions. You declare a function like so:

function functionName() {  // This is a "function declaration".
// Do something…
}

You can also define functions as expressions (function expressions), for example:

var x = function() {};

Aside: The above is technically an expression statement containing a function expression in an assignment expression.

There is also the strange case of function statements. These are defined in JavaScript as functions declared inside blocks. These were never supported in the standard, but browser vendors have historically implemented their own incompatible implementations.

if(true) {
function f() { // A function statement.
// Do something…
}
}

For this reason function statements should not be used in ES5 and below.

Note: ES6 does define standard behavior for function statements, the details of which are beyond the scope of this document.

Functions are objects (remember, everything is an object). And this is powerful: this means that functions can be returned from functions and passed into functions as arguments. We say functions are “first-class language citizens” in JavaScript.

Declaring a function can be thought of (1) as new’ing up an instance of the Function object behind the scenes.

var f = new Function('console.log("foo");');

(1) For introductory, explanatory purposes. There are differences in reality.

As an aside, for those with a little knowledge already, new Function will run the enclosed code in the global scope. Eval takes the local scope. setTimeout is an obfuscated Function instantiation.

Functions define scope in JavaScript.

if(true) {
var x = 1;
}
console.log(x); // 1 because functions define scope!

The above is equivalent to:

var x;
if(true) {
x = 1;
}
console.log(x);

In EcmaScript 6 there are additional types of variable declaration with block scoping: let and const.

if(true) {
let x = 1;
const y = 2;
}
console.log(x); // ReferenceError because x is scoped to the if-block.
console.log(y); // ReferenceError because y is scoped to the if-block.

Scope is important to understand because it governs the re-use of variable names and is used by the garbage collector to determine when something is unreachable and eligible for collection.

Summary

JavaScript has only two important concepts objects and functions. Everything is an object in JavaScript and this makes it very much an object-oriented language.

Value types are an optimization to enable better performance and less-surprising semantics for numbers, booleans and strings.

Scopes in JavaScript are defined by functions, although in the upcoming EcmaScript 6, blocks will provide scoping for certain types of variable declaration.

This series continues with User defined types and hoisting 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