JS: Nested functions gain a copy of the outer function's parameters?

Codesmith picture Codesmith · Dec 9, 2012 · Viewed 11.5k times · Source

So with the following code, I proved to myself that nested functions do indeed gain a copy of the parameters of the outer function:

    var o = {};
    (function(a,b,c,x){
        x.f = function(){
            return a.toString()+b.toString()+c.toString();
        }
    })(7,4,2,o);
    o.f();

The code yields

    742

Which means that the o.f function gains a copy of a,b, and c from the anonymous function. Otherwise, I would just get undefinedundefinedundefined

My questions are,

  • First, is this always true? or are there strict circumstances? (Like, must it be an object? etc.)
  • Also, what other kinds of obscure scopes exist like this in javascript? I'd love to know (i.e. what about third iterations?)
  • Lastly, I'd be perfectly fine with reading a document that explicates advanced concepts on javascript scopes. Does anyone know of any good ones?

Answer

Nathan Wall picture Nathan Wall · Dec 9, 2012

What you observe is called lexical scope. It means that the bindings of the variables in a certain scope in JavaScript are determined by where the variables appear in the code. It is always true, and it is true up to any level. The only main exception is the this value, which is dynamically scoped rather than lexically scoped. Dynamic scope means variables in the function depend on how and when the function is called. (See Lexical Scoping and Dynamic Scoping.)

Example:

var o = { 
    a: function() {
        var x = 5;
        console.log(this, x);
        function b() {
            console.log(this, x);
        }
        b();
    }
};

o.a();

The result of this example will be:

{Object o} 5
{window} 5

In other words, the first console.log will log this as a reference to the o object, while the second console.log will log this as a reference to the window object. However, both will log x as being equal to 5. The value of this is window when it is called in non-strict mode without a context. So this is not scoped lexically, but other variables, like x are. To read more about the behavior of this see A Short Overview of this.


To answer your questions directly:

1. First, is this always true? or are there strict circumstances? (Like, must it be an object? etc.)

Yes, it's true with the exception of this and arguments which change based on how the function is called. It doesn't have to be an object, all variables are lexically scoped.


2. Also, what other kinds of obscure scopes exist like this in javascript? I'd love to know (i.e. what about third iterations?)

You can go as deep as you want -- inner functions can always access the variables of their outer functions.

function a() {
    var x = 1;
    console.log('a:', x);
    return function b() {
        var y = 2;
        console.log('b:', x, y);
        return function c() {
            console.log('c:', x, y);
        };
    };
}

var foo = a();   // => logs 'a: 1'
var bar = foo(); // => logs 'b: 1 2'
bar();           // => logs 'c: 1 2'

This is actually part of another topic referred to as closures, which occur when you return a function from within another function.


3. Lastly, I'd be perfectly fine with reading a document that explicates advanced concepts on javascript scopes. Does anyone know of any good ones?

I've linked to a couple resources already. Another good one:

MDN: Functions and function scope (specifically the section on Nested Functions and Closures).

Also, you would be benefited from reading anything on closures, and you may also want to look up lexical scope.