سؤال

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?
هل كانت مفيدة؟

المحلول

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.

نصائح أخرى

All you need to do is read up on closures. http://en.wikipedia.org/wiki/Closure_(computer_science) And the inner function doesn't get a copy of that variable, it gets a reference to that variable. Example:

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

yields

842

Edit: Ok, maybe that's not all you need to need to read up on. I just figure it's worth demonstrating the concept is not foreign to many languages.

I made a silly mistake while using this concept in code and wrongly re-declared the parameter as a variable. Posting here in case it helps someone else.

(function(){
var outerFunction=function(varA){
    console.debug('value of varA in outer function '+varA);
    var innerFunction = function(){
        console.debug('value of varA in inner function '+varA);
    }
    innerFunction();
};
outerFunction("hi");

var buggyOuterFunction=function(varA){
    console.debug('value of varA in buggy outer function '+varA);
    var buggyInnerFunction = function(){
        var varA = varA || "hello"; //BUG - do not redeclare
        console.debug('value of varA in buggy inner function '+varA);
    }
    buggyInnerFunction();
};
buggyOuterFunction("hi");
})();

Output:

value of varA in outer function hi
value of varA in inner function hi
value of varA in buggy outer function hi 
value of varA in buggy inner function hello
  • yes, this is true for all types. This element of the javascript design is called "closure". Use that when googling for additional reading material.
  • There are no language limits on how deeply nested you can create functions, just maintainability limitations. Generally after the 2nd level you're getting into "closure hell" or "closure spaghetti" and it's time to refactor. So you could nest functions 4 levels deep and access function 1's parameters from the code within function 4.
    • 2 other "obscure" cases come to mind:
      • If you invoke a function directly without an explicit this binding, this is bound to the global (window) object. This is universally regarded as one of the egregious flaws in javascript. Beware.
      • JavaScript has "function hoisting" which means functions can be referenced from the top of a scope even if the actual function definition is further down in the source code file within the same scope.
  • Just read Douglas Crockford's JavaScript: The Good Parts. There are also many great tutorials and online books freely available and easily found with a web search. Just make sure they are modern (written no earlier than say 2010 for the most part).
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top