#Advanced Javascript

This page is about advanced Javascript. Javascript is a really weird language with a lot of history. (In other words, it's a hot freaking mess.) It started off as some random dude's side project, and thus the original implementation had a lot of holes. Soon, these holes were exploited into features, and now we pretty much accept the weird syntax as a part of Javascript.

EMCAScript 2015, or ES6, is the new proposed style of Javascript, and it fixes a huge volume of the weird syntax things that were present on old JS. However, it hasn't quite made it's away around to all the browsers yet. In large industrial software, it's a good idea to write all code in ES6 and just transpile it down to native JS - but on small websites, it's often still faster to just write old JS. It's important to know when reading old code, as well. This page does not cover any ES6. This page just addresses all the weird stuff that already existed in JS.

Also, this is written with browser Javascript in mind, not Node Javascript - but there are only minor differences, primarily with the global object.

Further resources:

Variables

Reference vs Value

Javascript does not have references (AKA pointers) explicitly built-in. You cannot create pointers for primitives, as you can in C.

Javascript passes all primitives by value, and all objects by reference. (Including arrays.) However, changing any object properties of an object referenced by variable changes the underlying object.

One way to think about this is that Javascript always passes by value, however, the value of an object is a reference. In other words, changing an object does not change the reference, even though it may change the underlying properties.

function f(a,b,c) {
    // a is a primitive, so it is re-assigned (doesn't point)
    // the object or primitive referenced by the original a is unchanged.
    a = 3;

    // calling b.push changes its properties (b is an object / array)
    // it adds a new property b[b.length] with the value "foo"
    // so, the object referenced by b has been changed
    // but the reference is unchanged (no new object created)
    b.push("foo");

    // the 'first' property of argument c has been changed
    // so the object referenced by c has been changed
    // but the reference is unchanged (no new object created)
    c.first = false;
}

var x = 4;
var y = ["eeny", "miny", "mo"];
var z = {first: true};
f(x,y,z);
console.log(x, y, z.first); // 4, ["eeny", "miny", "mo", "foo"], false

(An example of where a new object might be created would be in Java, where Strings are objects and not primitives. Many String operations actually result in the creation of a new String, which can quickly lead to memory issues - hence the StringBuilder class.)

Thinking of objects as just values of references allows us to explain what happens in the case of chained references. For instance, suppose we have an object inside of an array. The array accessor (array[i]) is a reference, and the object itself is a reference.

var a = ["1", "2", {foo:"bar"}];
var b = a[1]; // b is now "2"
var c = a[2]; // c now references {foo:"bar"}
a[1] = "4";   // a is now ["1", "4", {foo:"bar"}] 
// b still has the value it had at the time of assignment,
// since the primitive was passed as a strict value

a[2] = "5";   // a is now ["1", "4", "5"]
// c still has the value it had at the time of assignment,
// i.e. a reference to the object {foo:"bar"}

console.log(b, c.foo); // "2" "bar"

When we assign var c = a[2], we crawl through the references until we find a value. a is a reference, [2] is a reference, but {foo:"bar"} is the value - which is the value of a reference.

That sentence might not make any sense, but it basically means that changing a or its indices (the references that originally led to {foo:"bar"}) will not change the value of c, since the value that got passed was the reference to {foo:"bar"}. However, c now follows all the other normal rules of object referencing that we mentioned earlier.

Another way to think about this is to imagine that everything is a reference. Variables are just references to primitive literals, while objects contain references to primitives. This may or may not help you, depending on how you think. If you don't get it, just ignore this paragraph. References are really weird - there's all sorts of ways to think about them. The best way to understand them is to just work through examples and use them in your own code.

This can also cause problems if you want to do recursions on objects, such as when traversing a tree. In many tree algorithms, you want to keep a copy of your graph at all steps of your algorithm, but be able to manipulate the graph at certain steps, as well. If we simply passed the graph object to the next step of the recursion, every step would point to the same object reference. The way to solve this would be to copy the object.

var obj1 = {
  foo: 'bar',
  apple: 'orange'
};

//the value of the object itself is copied, not its properties 
//(i.e. it becomes a reference)
var obj2 = obj1; 
//the object is copied, so all properties are duplicated
var obj3 = JSON.parse(JSON.stringify(obj1));
//the property in the original object is changed, changing the underlying value
obj1.foo = 'berry';

//the reference to the object also has its value changed
console.log(obj1.foo, obj2.foo, obj3.foo); //'berry', 'berry', 'bar'

Unfortunately, copying objects in Javascript is very difficult, due to the way that prototypes work. (Another reason why Javascript is a mess...) If you want to run a strict deep copy, you'd have to copy the object and all its prototypes. Many times, you will have to write your own copy function to suit your specific needs. The JSON.stringify trick will give you a deep copy of any enumerable parameters, but will not touch prototypes.

In fact, it will change the prototype to the default Object.prototype, since you're parsing an object from a string, which is pretty much using an object literal. You can use Object.create to keep prototypes, and you also get a much more elegant syntax. It's not supported before IE8, but you can use use a polyfill for that. Most of the time, use Object.create - it's much more readable and is better style. Also, parsing from strings can open up vulnerabilities in larger applications, since a hacker could parse in their own JSON to break the application.

Assignment Operators

Assignment operators actually have return values.

var x=10;
console.log(x=11); //11
console.log(x); //11

var i = 10;
console.log(i++); //10
console.log(i); //11

var j = 10;
console.log(++j); //11
console.log(j); //11

var k = 10;
console.log(k+=2); //12
console.log(k); //12

This can cause all sorts of weird things to happen if you're not careful. For good coding's sake, please try to avoid using these return values.

//GOOD
function add() {
  counter+=1;
  return counter;
}

//BAD
function add() {
  return counter+=1;
}

Variable Names

For some reason, Javascript allows many unicode characters into variable names. Within ASCII, a variable can have any characters of the regex [a-zA-Z_{DOLLARSIGN}][0-9a-zA-Z_$]*. So in libraries like jQuery, there's nothing special about $. It's just a variable name.

(note: My typesetting engine is giving issues with the $ symbol... I'll have to fix this later.)

Most unicode characters work, however...


var ಠ_ಠ = 'myface';
var π = Math.PI;
var ლ_ಠ益ಠ_ლ = 42;

.
.

var jͬà̄̂v̓a̛̐͐̀͌̏͛̐s͛ͭͭ̍ͦ̐͝c̷͗ͭͫ̐ŕ̓̀í͐͐͆ͦ́pͭ̉́̌̈́͆ͥţ̆͐͂̅d̊̐ͣeͧ̑̆͑̊͝mͮͩ̓ͨ̀ͣ͠ǒ̸ͪ̎̃̑n͡ = 'me';

.
.

//Yep!

See https://mathiasbynens.be/notes/javascript-identifiers for more details. https://mothereff.in/js-variables has a tool that validates identifiers.

Variable Types

Javascript only has five variable types: boolean, number, string, object, and undefined. This simplicity has some beauty, to the extent that values can be cast and concatenated in all sorts of weird ways without needing to parse first. The bad part is that occasionally Javascript will misunderstand your parsing. For instance, in this case you have to be careful to explicitly parse into integers.

$("#value1").val(); //'1'
$("#value2").val(); //'2'
$("#value1").val()+$("#value2").val(); //'12'
parseInt($("#value1").val())+parseInt($("#value2").val()) //3

boolean, number, and string are primitives. They are the building blocks of all objects, and are thus immutable. You can't say true=false, or 1=2. They can convert to and from each other, but style guides will recommend that you do this explicitly rather than implicitly to avoid problems.

!'' == true
!!0 == false
(!'hi') % 2 == 0

Please don't ever do any of this. Ever. It can occasionally be acceptable for numbers, since true == 1 and false == 0 is standard in many other programming languages, but just try to avoid it if you want to keep your code bulletproof. Use ternary operators instead.

Javascript objects are their own thing which won't be covered in this section, but encapsulate pretty much every complex data type out there. undefined the type for anything that is, well, not defined. If I've never declared the variable myApple, then myApple is undefined.

Technically, I left out one type. function is also a type in Javascript - and since Javascript is a functional programming language, for all intents and purposes that's also a variable type. You can declare functions using variable notation. You can pass functions into functions. A function can be used as a prototype, or a class of sorts. That won't be covered in this section, but just keep that in mind if you ever call a constructor. They are also passed by reference, since they're basically objects. See the function section for more details.

typeof Date == 'function' //true

You can check the type of an object with the typeof operator.

typeof 1 == 'number'
typeof asdfadsf == 'undefined' //unless you've defined it for some reason...
typeof {} == 'object'

asdfasdf == null
typeof asdfasf = 'object' //huh?

Wait, what...? Why is the 'null' type an object? Well...

The following quote from the book Professional JS For Web Developers (Wrox) pretty much summarizes the mess that is modern Javascript:

"You may wonder why the typeof operator returns 'object' for a value that is null. This was actually an error in the original JavaScript implementation that was then copied in ECMAScript. Today, it is rationalized that null is considered a placeholder for an object, even though, technically, it is a primitive value."

The 'Number' Type

Javascript numbers extend the real numbers. There's a +0 and -0. This is so that 1/0 = Infinity and 1/-0 = -Infinity

+0 == -0 //True
1/+0 == 1/-0 //False

The weird thing is, NaN is also an acceptable number type. (In other words, typeof NaN == 'number'). This clearly has some logical inconsistencies, but don't think too hard about it. Basically, if you do any arithmetic operation on two numbers, you get a number. If that operation happens to not be valid, instead of returning undefined or null, Javascript gives you NaN. However, you can't compare things to NaN - you muse use isNaN().

1/0 == Infinity //True
0/0 == NaN  //False
isNaN(0/0) //True

Boolean Expressions

&& and || actually have more specific definitions beyond simple boolean comparisons. expr && expr2 has the following formal definition:

expr1 && expr2:
  returns expr1 if it can be converted to false,
  otherwise returns expr2

Similarly:

expr1 || expr2:
  returns expr1 if it can be converted to false,
  otherwise returns expr2

So, in other words, true && myExpr always evaluates to myExpr. This means in templating interfaces like React, we can do fancy stuff like:

return (
  <div>
  {unreadMessages.length > 0 &&
    <h2>
      You have {unreadMessages.length} unread messages.
    </h2>
  }
  </div>
)

Objects

Prototypes

Javascript has no classes in the traditional sense. Instead, functions can be instantiated as objects.

// var Person = function(firstName, last) { ... } //equivalent expression
function Person(firstName, last) {

    this.firstName = firstName; //unambiguous use of variable names
    this.lastName = last; 

    this.firstInitial = firstName.slice(0, 1);
    this.speak = function() {
        // console.log(firstName); //not good - will return input of constructor
        // console.log(firstInitial); //ReferenceError
        console.log(this.firstName); //good

        //ALWAYS refer to interal properties with 'this'
    }   
}

var myPerson = (new Person('bob', 'smith'));
myPerson.speak(); //outputs 'bob'

Again, note that Javascript does not have classes. This odd functional notation is something entirely different and weird.

In traditional programming languages, you create classes as a blueprint and objects as instances. In Javascript, prototypes are like the "original object" and instances are copies of it.

function Fruit(type) {
    this.fruitType = type;

    this.whatFruit = function() {
        console.log("Really, I am a "+this.fruitType);
    }
}

Fruit.prototype; //Object {constructor: function...}
Fruit.prototype.whichFruit = function() {
    console.log("Indeed, I am a "+this.fruitType);
}//Now Fruit.prototype is: Object {whichFruit: function, constructor: function}

var apple = new Fruit("apple");
apple.whatFruit();
apple.whichFruit(); //both of these execute just fine - but one is "hidden" because it's attached to a parent

Object.keys(apple); //'["fruitType", "whatFruit"]'
//doesn't show "whichFruit" - the prototype properties are not enumerable

This example demonstrates the primary use of prototypes in Javascript, which is inheritance. whatFruit() and whichFruit() both execute totally fine, but if you inspect apple in the console, you'll notice that whichFruit is not defined. That's because it's defined in the prototype.

We can model object inheritance in Javascript as a chain of prototypes. Every single object in Javascript has a prototype, even the prototypes themselves. The only exception is the 'root' object: Object.prototype. This is the big momma of all objects, and it's where implicit functionality like toString() comes from.

When you access a property in Javascript, the property is searched for in the object, then on the object's prototype, then on the prototype's prototype, and so on until Object.prototype. Properties earlier on the chain (farther from the root) can thus override the ones later in the chain (closer to the root).

var x = {
  toString = function() {
    return 'overridden';
  }
};

x.toString(); // 'overridden'

y = {};
y.toString(); // '[object Object]'

Prototypes are instances, rather than blueprints - again, this is the main difference between class-based and prototype-based inheritance.

Fruit.prototype.whichFruit = function() {
    console.log("Now, I am an old "+this.fruitType);
}
var orange = new Fruit("orange");
apple.whichFruit();  //'Now, I am an old apple'
orange.whichFruit(); //'Now, I am an old orange'

Both of these have changed - even though one was instantiated after and the other before the prototype delcaration. The "prototype" property thus carries a reference to a "parent" object - it's attached to the function from which these objects were created.

Object literals ({}) and Object() constructors carry the global Object.prototype, whereas function-defined objects by default carry one more prototype, which is essentially a reference to the function from which they are defined.

var pear = {type: 'pear'}; 
console.log(Object.getPrototypeOf(pear)); 
//Object {__defineGetter__: function, ... } (default)
console.log(Object.getPrototypeOf(apple));
//Object {whichFruit: function, constructor: function}

Object.getPrototypeOf(pear) == Object.getPrototypeOf(Object.getPrototypeOf(apple)); //true
Object.getPrototypeOf(pear) == Object.prototype //true
Function.prototype.__proto__==Object.prototype //true - even functions are just objects (kind of)

You can't directly access prototypes, but some browsers (like Chrome) have a __proto__ pseudo-property which can be used to explore the prototype. If you inspect pear and apple in Chrome, you'll notice pear has a one-step chain while apple has a two-step chain, since pear uses the Global prototype but apple has one more step.

Let's take a look at another simple example just for illustration.


function Plant(country, size) {
    this.country = country;
    this.size = size;
    this.showCountry = function() {
        console.log("from "+this.country);
    }
}
Plant.prototype.showSize= function() {
    console.log("of size "+this.size);
}
console.log(Object.keys(new Plant())); 
//does not have showSize, only showCountry

function Tree(treeName, treeColor) {
    this.name = treeName;
    this.color = treeColor;
    this.iLoveTrees = function() {
        console.log("I love trees!");
    }
}

// this line now establishes an inheritance
var x = new Plant("Mexico","small");
Tree.prototype = x;
/*
Tree.prototype = {
  country: "Mexico",
  size:"Big", 
  showCountry:function(){...}
  }; 
*/ //this notation also works totally fine

var anEvergreen = new Tree("evergreen", "green");
anEvergreen.showCountry(); // 'from Mexico'
anEvergreen.showSize(); // 'of size small'

Tree.prototype = new Plant("argentina","big");
anEvergreen.showCountry();
anEvergreen.showSize(); 
//does not change the output - prototypes are declared at object initialization

anEvergreen.iLoveTrees();
anEvergreen.showCountry();
console.log(anEvergreen.toString()); 
//3 different object layers - none of these throw an error

In this example, prototypes are shown to be declared at object initialization. The Object API actually gives you some ways to mess with this, but you should really never have to do that.

The confusion that often arises with prototypes usually stems from the ambiguity between functions and objects in Javascript.

function Foo() {
  this.kind = 'foo'
}

var foo = new Foo(); 
foo.kind // 'foo'

//this is what this looks like behind the scenes
function Foo() { 
  var this = {}; // this is not valid notation, just for illustration
  this.__proto__ = Foo.prototype;

  this.kind = 'foo'
  return this;
}

The confusion is because "prototype" refers to two things. Each object has a prototype. Each function has a prototype.

foo.__proto__ === Foo.prototype // true
Foo.__proto__ === Foo.prototype //false
// Foo is an instance of the 'function' prototype (kind of)

The 'prototype' property of a function points to the object that will be assigned as the prototype of instances created with that function when using the keyword new.

function Person(name) {
  this.name = name;
}

// the function person has a prototype property
// we can add properties to this function prototype
Person.prototype.kind = 'person'

// when we create a new object using the 'new' keyword...
var zack = new Person('Zack');
// ...the prototype of the new Person object by default points to Person.prototype
zack.__proto__ == Person.prototype // true

// in the new object we have access to properties defined in Person.prototypes
zack.kind // person

Think this is a mess? It only goes deeper... https://johnresig.com/apps/learn/#65

//1.3 'this'

//1.4 'Object' API

Functions

Function Scope

By definition, scope is the set of variables, objects, and functions you have access to. Variables are function-scoped in Javascript. If you're an advanced programmer, you should be familiar with variable context by now. Certain variables only exist within certain bits of code. Perhaps the most ubiquitous example is the for loop.

for(var i=0; i<10; i++) {
  console.log(i);
}
console.log(i); //10 - doesn't crash

Huh? What's that? In most languages, such as Java and C, the last line would throw an error. (The distinction often comes between scripting and programming languages. No error is thrown in Python.) Java and C are block-scoped, while Javascript is function-scoped. Variables exist within code blocks (inside {}) in Java, but inside functions in Javascript.

The exception is if you use the implicit variable declaration notation. Strict mode prevents this notation.

myFunction();

// code here can use myVariable 

function myFunction() {
    myVariable = "a variable";
}

Variables created without a keyword are always global, no matter their scope.

Global variables belong to the window object.

var x = 10;
window.x == x //true

Occasionally, you will see anonymous functions used in order to encapsulate variables and prevent memory leaks.

(function() {
  for(var i=0; i<10; i++) {
    console.log(i);
  }
})();

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

The other use of anonymous functions is when you have clashing variables. If you're writing your own libraries and using context properly, this will rarely be an issue. However, let's say you're using two separate libraries. It's very common to give a function the $ variable as a shorthand. Both jQuery and Prototype do this. If you want to use both, you could easily call the functions directly...

jQuery('a').click(function() {
  if(jQuery(this).attr('id')=='myId') {
    jQuery(this).addClass('clicked');
    ...
  }
});

...but this can quickly get messy. You can fix this by using an anonymous function call.

(function($) {
  $('a').click(function() {
    if($(this).attr('id')=='myId') {
      $(this).addClass('clicked');
      ...
    }
  });
})(jQuery);

When nesting functions, keep in mind that for any Javascript function, a function has access to local variables, global variables, and variables from higher scopes. Functions can access 'parented' scopes. More detail on this in the section on closures.

var layer1 = 1;

function f1() {
  var layer2 = 2;
  function f2() {
    var layer3 = 3;
    console.log(layer1+" "+layer2+" "+layer3); //'1 2 3'
    function f3() {
      var layer4 = 4;
      console.log(layer1+" "+layer2+" "+layer3); //'1 2 3'
    }
    f3();
  }
  f2();
}
f1();

Hoisting

In Javascript, you can use functions before you declare them.

greet(); //executes fine

function greet() {
  alert('hello!')
}

This seems like really odd behavior, but in Javascript, where scripts are sometimes loaded in weird orders, this can resolve a few errors.

What happens is that every function declaration gets 'hoisted' to the top of its scope. (Technically, no - declarations just get run before everything else in compile time.) Similar behavior occurs with variables.

var x = 10;
var y = 5;
console.log(x); //'10'
console.log(y); //'5'

var a = 10;
console.log(a); //'10'
console.log(b); //'undefined' - but does not throw an error
var b = 5;

Huh? Why is b undefined? I thought the declaration executes first? In this case, only the declaration var b gets hoisted, while the initialization of b=5 does not. The second code block is equivalent to the following.

var a = 10;
var b;
console.log(a); //'10'
console.log(b); //'undefined'
b = 5;

It's often good practice to maintain readability and avoid hoisting by using variable declaration notation for functions.

greet();

var greet = function() {
  alert('hello'); //TypeError: greet is not a function
}

Closures

Let's say we want to build a counter. For instance, we want the track the number of clicks on <a> tags throughout the page.

$('a').click(function() { 
  add();
});

We could create a global variable and access that on each click.

var clickCount = 0;
function add() {
  clickCount+=1;
}
add(); //clickCount = 1;
add(); //clickCount = 2;

However, this would allow clickCount to be changed elsewhere in the program. In general, having unnecessary global variables is bad practice, as you may get clashes between libraries or accidentally change a global variable whose name you've forgotten.

We need to put clickCount into a scope. However, we can't simply toss it into a function.

function add() {
  var clickCount = 0;
  clickCount+=1;
}
add(); //clickCount = 1;
add(); //clickCount = 1;

This would reset the value each time. We need a way to execute var clickCount only once. We could put an inner function that we use to access counter.

function add() {
    var counter = 0;
    function plus() {
      counter += 1;
    }
    plus();    
    return counter; 
}

But now, plus() is inaccessible from outside of the add() function.

The solution is some self-invoking function hackery that we now treat as part of the standard Javascript developer's toolkit. (Remember I said Javascript is a hot mess?) It's called a closure. Learn to use it, learn to love it.

var add = (function () {
    var counter = 0;
    return function () {
      counter += 1;
      console.log('counter is '+ counter);
    }
})();

add(); //the counter is 1
add(); //the counter is 2
add(); //the counter is 3

console.log(counter) //counter not defined

Let's break down what's happening.

On the declaration of add, we have a self-executing anonymous function which returns a function. (Deal with it.) We declare the counter variable and set it at 0.

We then return a function which increments counter by 1. Each time we call add(), this counter will now be properly incremented by 1.

In order to use the counter variable, we have to expose it. In the previous example, we're just printing it to the console, but you can't really use that in your code. Instead, we can return the value to expose it.

var add = (function () {
    var counter = 0;
    return function () {
      counter += 1;
      return counter;
      //return counter+=1; //equivalent, but terrible
    }
})();

So, why the hell can we do this? How can we access a variable in a scope after the function is done executing? Didn't I previously say that variables are killed after the scope expires?

It might help to recall that when data on a computer is 'deleted', its values aren't reassigned to garbage data. The data is just marked as unused. Similarly, when a variable declaration expires, the memory location itself doesn't get eliminated - the link between the identifier and the memory address is broken.

One way to conceptualize it is that every function has an associated scope chain. When the function is created, its scope carries a reference to every variable that was available at the time of function creation.

So when a closure is created, we initialize an anonymous inner function that has access to the local variables of its containing scopes. We then expose this function to the outer scope by returning it, allowing us to access these inner variables through the function, even if we cannot access them directly.

This also means that you can even nest closures, since you can access multiple layers of local scopes...

var addFour = (function () {
    var sum = 0;

    var add = (function () {
      return function () {
        sum += 1; //retains access to variables two functions outward
        return sum;
      }
    })();

    return function () {
      add(); add(); add();
      return add();
    }
})();

console.log(addFour()); //4
console.log(addFour()); //8
console.log(addFour()); //12

...but please don't do this.

Unless you have to. But try to avoid it.

Okay, cool. That's kind of weird and fun, I guess. Why do I care?

We saw in the previous example that closures can grant data privacy to inner variables which normally would not exist in Javascript.

The other important use is as a 'function creator'. A function that creates more functions. This is one of the cleanest and most satisfying things to learn when you first get into functional programming.

Let's take a real-world example that I actually use in a number of physics demos. Here's the function in question:

function makePushField(ox, oy, power) {
    var field = function(x, y) {
        var magnitude = Math.pow(ox-x, 2)+Math.pow(oy-y, 2);
        return {x:power*(x-ox)/magnitude, y:power*(y-oy)/magnitude};
    }
    return field;
}

Here, we're creating an outward-pushing gravitational vector field at the point (ox, oy) with power power. We take these parameters into account, which is possible because the inner function can access the arguments from the outer function's scope, and use these parameters to construct a function which can then be returned. In Java, you might have to create an object to encapsulate each function, adding unnecessary verbosity.

Closures can also be used to create 'stateful' functions. From a programming theory standpoint, a 'stateless' function always has the same output given the same input. 'Stateful' thus refers to the opposite. Our 'count' example is a stateful function with data privacy.

It's important to note that you don't need closures for anything, but they can make life a lot easier. Nothing's stopping you from treating Javascript like Java, but you'd be losing out on a lot of functionality.

There's a common issue you need to watch out for when using closures. This example is taken from MDN.

function showHelp(help) {
  document.getElementById('help').innerHTML = help;
}

function setupHelp() {
  var helpText = [
      {'id': 'email', 'help': 'Your e-mail address'},
      {'id': 'name', 'help': 'Your full name'},
      {'id': 'age', 'help': 'Your age (you must be over 16)'}
    ];

  for (var i = 0; i < helpText.length; i++) {
    var item = helpText[i];
    document.getElementById(item.id).onfocus = function() {
      showHelp(item.help);
    }
  }
}

setupHelp();

This snippet fills in help text depending on the input you select. However, if you run this, you'll find that only the text about age ever shows up.

This is because var is function-scoped. Functions are created with the function content and an associated lexical environment. Since each closure attached to the onfocus handler has the same lexical environment, the function-scoped i ultimately refers to the final value of i after the loop runs, which is the last item in the array.

This can be solved with an anonymous closure.

function showHelp(help) {
  document.getElementById('help').innerHTML = help;
}

function setupHelp() {
  var helpText = [
      {'id': 'email', 'help': 'Your e-mail address'},
      {'id': 'name', 'help': 'Your full name'},
      {'id': 'age', 'help': 'Your age (you must be over 16)'}
    ];

  for (var i = 0; i < helpText.length; i++) {
    (function() {
       var item = helpText[i];
       document.getElementById(item.id).onfocus = function() {
         showHelp(item.help);
       }
    })(); // Immediate event listener attachment with the current value of item (preserved until iteration).
  }
}

setupHelp();

Or using the let keyword in ES6, which is not covered here.

function showHelp(help) {
  document.getElementById('help').innerHTML = help;
}

function setupHelp() {
  var helpText = [
      {'id': 'email', 'help': 'Your e-mail address'},
      {'id': 'name', 'help': 'Your full name'},
      {'id': 'age', 'help': 'Your age (you must be over 16)'}
    ];

  for (var i = 0; i < helpText.length; i++) {
    let item = helpText[i];
    document.getElementById(item.id).onfocus = function() {
      showHelp(item.help);
    }
  }
}

setupHelp();