Antonio Ortiz Antonio Ortiz - 4 months ago 22
Javascript Question

Lexical scope/closures in javaScript

I understand functions in 'js' have lexical scope (i.e. functions create their environment (scope) when they are defined not when they are executed.)

function f1() {
var a = 1;
f2();
}

function f2() {
return a;
}
f1(); // a is not defined


When I run just 'f()' it returns the inner function. Which I get, that's what 'return' does!

function f() {
var b = "barb";
return function() {
return b;
}
}
console.log(b); //ReferenceError: b is not defined


Why do you get 'ReferenceError: b is not defined?'
But doesn't the inner function above have access to it's space, f()'s space etc. Being that 'b' is being returned to the global space, wouldn't the console.log() work?

However when I assign 'f()' to a new variable and run it:

var x = f();
x();// "barb"
console.log(b); //ReferenceError: b is not defined


This returns 'b' which is "barb", but when you run console.log() again you'll get 'ReferenceError: 'b' is not defined'; Isn't 'b' in the global scope now since it has been returned? SO why didn't 'x()' also return the inner function just like 'f()' did?

Answer

You, my friend, are thoroughly confused. Your very first statement itself is wrong:

functions create their environment (scope) when they are defined not when they are executed

Actually it's the opposite. Defining a function doesn't create a scope. Calling a function creates a scope.

What's a scope?

To put it simply, a scope is the lifespan of a variable. You see, every variable is born, lives and dies. The beginning of a scope marks the time the variable is born and the end of the scope marks the time it dies.

In the beginning there's only one scope (called the program scope or the global scope). Variables created in this scope only die when the program ends. They are called global variables.

For example, consider this program (it's not JavaScript):

x = 10       // global variable x

{            // beginning of a scope
    x = 20   // local variable x
    print(x) // 20
}            // end of the scope

print(x)     // 10

Here we created a global variable called x. Then we created a block scope. Inside this block scope we created a local variable x. Since local variables shadow global variables when we print x we get 20. Back in the global scope when we print x we get 10 (the local x is now dead).

Block Scopes and Function Scopes

Now there are two main types of scopes in programming - block scopes and function scopes.

The scope in the previous example was a block scope. It's just a block of code. Hence the name. Block scopes are immediately executed.

Function scopes on the other hand are templates of block scopes. As the name suggests a function scope belongs to a function. However, more precisely, it belongs to a function call. Function scopes do not exist until a function is called. For instance:

var x = 10

function inc(x) {
    print(x + 1);
}

inc(3);   // 4
print(x); // 10
inc(7);   // 8

As you can see every time you call a function a new scope is created. That's the reason you get the outputs 4, 10 and 8.

JavaScript only has function scopes. It doesn't have block scopes. Hence if you want to create a block scope then you need to create a function and immediately execute it:

var x = 10;     // global variable x

(function () {  // beginning of a scope
    var x = 20; // local variable x
    print(x);   // 20
}());           // end of the scope

print(x);       // 10

This pattern is called an immediately invoked function expression (IIFE).

Lexical Scopes and Dynamic Scopes

Function scopes can again be of two types - lexical and dynamic. You see, in a function there are two types of variables:

  1. Free variables
  2. Bound variables

Variables declared inside a scope are bound to that scope. Variables not declared inside a scope are free. These free variables belong to some other scope, but which one?

Lexical Scope

In lexical scoping free variables must belong to a parent scope. For example:

function add(x) {         // template of a new scope, x is bound in this scope
    return function (y) { // template of a new scope, x is free, y is bound
        return x + y;     // x resolves to the parent scope
    };
}

var add10 = add(10);      // create a new scope for x and return a function
print(add10(20));         // create a new scope for y and return x + y

JavaScript, like most programming languages, has lexical scoping.

Dynamic Scope

In contrast to lexical scoping, in dynamic scoping free variables must belong to the calling scope (the scope of the calling function). For example (this is also not JS - it doesn't have dynamic scopes):

function add(y) {   // template of a new scope, y is bound, x is free
    return x + y;   // x resolves to the calling scope
}

function add10(y) { // template of a new scope, bind y
    var x = 10;     // bind x
    return add(y);  // add x and y
}

print(add10(20));   // calling add10 creates a new scope (the calling scope)
                    // the x in add resolves to 10 because the x in add10 is 10

That's it. Simple right?

The Problem

The problem with your first program is that JavaScript doesn't have dynamic scoping. It only has lexical scoping. See the mistake?

function f1() {
    var a = 1;
    f2();
}

function f2() {
    return a;
}

f1(); // a is not defined (obviously - f2 can't access the `a` inside f1)

Your second program is a very big mess:

function f() {
    var b = "barb";

    return function() {
        return b;
    }
}

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

Here are the mistakes:

  1. You never called f. Hence the variable b is never created.
  2. Even if you called f the variable b would be local to f.

This is what you need to do:

function f() {
    var b = "barb";

    return function() {
        return b;
    }
}

var x = f();

console.log(x());

When you call x it returns b. However that doesn't make b global. To make b global you need to do this:

var x = f();
var b = x();
console.log(b);

Hope this helped you understand about scopes and functions.