Posted on

Scoping in Javascript With ‘Let’

In my post yesterday about multiple-values I used the keyword ‘let’ in some of the examples. This is another feature of the Mozilla Javascript platform. I assume it is named after ‘let’ from Lisp, because it performs the same task: letting you bind values in block scope.

As you know, the scope of variables in Javascript is the entire function in which they appear. So take a trivial function like this.

function foo() {
    var f = 10;
    print(f);

    {
        var f = 20;
        print(f);
    }

    print(f);
}

When we call the function the output will be

    10
    20
    20

Our rebinding of ‘f’ inside the inner block changes ‘f’ on the outside. So unlike a language like Perl, we cannot use arbitrary blocks to control scope. At least not without ‘let’. We can rewrite the above like so:

function foo() {
    var f = 10;
    print(f);

    {
        let f = 20;
        print(f);
    }

    print(f);
}

When we run this we get

    10
    20
    10

The binding we create with ‘let’ is only in effect for the lexical block in which it appears. Once that block ends, the previous value is restored. There are many apt adjectives for describing this behavior. I opt for ‘delicious’.

My example above uses what is called a ‘let definition’. That is when you use ‘let’ inside of a block to define a variable in the same way you would ‘var’. There are two other uses of ‘let’. First there is the ‘let statement’, which looks like this:

function foo() {
    var f = 10;
    print(f);

    let (f = 20) {
        print(f);
    }

    print(f);
}

This has the same effect as the previous example. The difference between let statements and let definitions is stylistic. Let statements look better (in my opinion) when the only function of the block is to introduce some temporary bindings. If you need to do to for an existing block, then let definitions are more appropriate. For example, consider the difference between

// A let statement

if (foo === 20) {
    let (bar = foo + 10) {
        ...
    }
}

and

// A let definition

if (foo === 20) {
    let bar = foo + 10;
    ...
}

We save on some indentation.

The third type of ‘let’ is the ‘let expression’. It is used to introduce bindings that are scoped to a single expression. Like so:

var x = 10;
var y = 20;

print( let (x = 5, y = 5) x + y );    // Prints 10
print( x + y );                       // Prints 30

The right side of a ‘let’ references the scope outside of the ‘let’, which means if we are rebinding an existing variable, we can use its current binding to do so. As an example:

var foo = 10;

let (foo = foo + 500, bar = foo) {
    print(foo)    // Prints 510
    print(bar)    // Prints 10
}

The ‘foo’ on the right side of both bindings refers to the ‘foo’ from the outer scope.

In all cases, ‘let’ creates a fresh binding when it is encountered. This is very important when creating closures over values. Consider this loop that creates a series of <li> elements, each with an onclick event:

var list = document.getElementById("list");

for (var i = 1; i <= 5; i++) {
    var item = document.createElement("li");

    let j = i;

    item.onclick = function (event) {
        alert("Item " + j + " was clicked");
    };

    list.appendChild(item);
}

Each onclick value closes over ‘j’, so that they will print out ‘Item 1’, ‘Item 2’, and so on when clicked. Because ‘let’ always creates a fresh reference, we get the expected behavior. If we wrote var j = i instead then all five elements would print out ‘Item 5 was clicked’. This is because they would all point to the same ‘i’, whose value is changed by the for-loop. We would have this same problem if we also used ‘i’ from inside the ‘onclick’ function. When we create a closure over a variable with a changing value, using ‘let’ is the only way to create a snapshot of the current value at that time.

So the next time you want to get your lexical scope on, don’t forget that you have ‘let’ available.

Posted on

Returning Multiple Values

Mozilla’s Javascript platform supports returning multiple values, as of version 1.7. I don’t know when that got incorporated into the XUL platform, but the features from 1.7 are usable there. In fact, we already use this in ColorFish:

this.getHSL = function () {
    var h, s, l;

    // ...

    return [h, s, l]
}

The last line returns an array with three values. This is how we pass back multiple values. If we are only interested in one return value then we can use this approach:

this.getHue = function(){
    return this.getHSL()[0];
}

this.getSaturation = function(){
    return this.getHSL()[1];
}

this.getLightness = function() {
    return this.getHSL()[2];
}

We can also get all of the values using destructuring binding. That is a fancy way of saying that when we assign a value, the left side of the assignment describes the shape of the value on the right side. For example:

var [h, s, l] = getHSL();
print(h);
print(s);
print(l);

On the first line, the left side matches the shape of the right side, the three-value array returned by getHSL(). If needed, we can ignore certain values.

var [h, , l] = getHSL();

// This is also legal if we don’t care about return values.  The
// difference between this and just calling getHSL() is that this
// makes it clear we are ignoring the return values.

var [,,] = getHSL();

This mechanism is intended to be the main way we pass back multiple-values in Javascript on the Mozilla platform.

But destructuring has other uses. The multiple assignments happen in parallel, not in sequence. So we can use destructuring to swap values without the need for temporary variables.

[a, b, c] = [c, a, b]

// Now a = c, b = a, and c = b

We can also use destructuring to get at objects.

var Lobby = { first: “Lobby”, last: “Jones” };

for (let [name, value] in Lobby) {
    // ...
}

Here we pull out each name and value of the properties as a pair. If we have a complex object, we can use an object on the left side of our bind to describe only the properties we want to extract into variables. Here is a good example from Mozilla’s site:

var people = [
  {
    name: "Mike Smith",
    family: {
      mother: "Jane Smith",
      father: "Harry Smith",
      sister: "Samantha Smith"
    },
    age: 35
  },
  {
    name: "Tom Jones",
    family: {
      mother: "Norah Jones",
      father: "Richard Jones",
      brother: "Howard Jones"
    },
    age: 25
  }
];

for each (let {name: n, family: { father: f } } in people) {
    print("Name: " + n + ", Father: " + f);
}

The key part here is the object sturcture to the left of ‘in’.

{
    name: n,
    family: {
        father: f
    }
}

This tells Javascript to pull out the name property and bind its value to ‘n’, and to take out the father property from inside the family object and bind that to ‘f’. This object is just a partial blueprint of the structure of ‘people’, naming the properties that we care about.