122

I got a piece of code for javascript which I just do not understand:

function dmy(d) {
    function pad2(n) {
        return (n < 10) ? '0' + n : n;
    }

    return pad2(d.getUTCDate()) + '/' +
       pad2(d.getUTCMonth() + 1) + '/' +
       d.getUTCFullYear();
}

function outerFunc(base) {
    var punc = "!";

    //inner function
    function returnString(ext) {
       return base + ext + punc;
    }

    return returnString;
}

How can a function be defined within another function? Can we call pad2() from outside of my() function?

Please put some light on it. Thanks

Erik Dekker
  • 2,395
  • 5
  • 33
  • 55
Thomas
  • 33,544
  • 126
  • 357
  • 626

8 Answers8

164

Functions are another type of variable in JavaScript (with some nuances of course). Creating a function within another function changes the scope of the function in the same way it would change the scope of a variable. This is especially important for use with closures to reduce total global namespace pollution.

The functions defined within another function won't be accessible outside the function unless they have been attached to an object that is accessible outside the function:

function foo(doBar)
{
  function bar()
  {
    console.log( 'bar' );
  }

  function baz()
  {
    console.log( 'baz' );
  }

  window.baz = baz;
  if ( doBar ) bar();
}

In this example, the baz function will be available for use after the foo function has been run, as it's overridden window.baz. The bar function will not be available to any context other than scopes contained within the foo function.

as a different example:

function Fizz(qux)
{
  this.buzz = function(){
    console.log( qux );
  };
}

The Fizz function is designed as a constructor so that, when run, it assigns a buzz function to the newly created object. That is, you'd use it like this:

const obj = new Fizz();
obj.buzz();

or more concisely (if you don't need to keep the object after calling buzz):

new Fizz().buzz();
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
zzzzBov
  • 174,988
  • 54
  • 320
  • 367
  • What is window.baz = baz? Why does this line m?ke baz available? – ZZZ Aug 13 '17 at 16:09
  • @ZiyangZhang, the paragraph after that code block has the explanation, was there a particular part that is unclear? – zzzzBov Aug 13 '17 at 16:21
  • Perhaps this does different things depending on 'strict'? – David Spector Jun 07 '21 at 20:01
  • Does JavaScript have to recreate "bar" and "baz" every time you call "foo?" –  Dec 03 '21 at 19:11
  • 1
    @ZiyangZhang `window` is a global variable so its properties, including `baz` in the example above, are available everywhere. See https://developer.mozilla.org/en-US/docs/Web/API/Window – Caltor Feb 09 '22 at 11:40
42

It is called closure.

Basically, the function defined within other function is accessible only within this function. But may be passed as a result and then this result may be called.

It is a very powerful feature. You can see more explanation here:

javascript_closures_for_dummies.html mirror on Archive.org

Dan Cornilescu
  • 39,470
  • 12
  • 57
  • 97
Tadeck
  • 132,510
  • 28
  • 152
  • 198
15
function x() {}

is equivalent (or very similar) to

var x = function() {}

unless I'm mistaken.

So there is nothing funny going on.

Andreas
  • 2,261
  • 1
  • 17
  • 25
  • 8
    The first syntax will be moved to the start of the document. so it is possible to call function 'x' before the funtion is initialized. – Tom Nov 13 '13 at 11:07
  • 10
    The first syntax will also get you much nicer stack traces with named functions, the second will give you a headache – TheZ Oct 22 '14 at 19:25
  • @TheZ I think Chrome recently added function name inference to debugging so you don't get the same headache as before in the common case. – jinglesthula Dec 05 '18 at 21:24
  • Lots of funny things are going on. Any variables used in an inner function are captured as snapshots. I think. And maybe referenced global variables are also captured. – David Spector Jun 07 '21 at 20:12
10

Function-instantiation is allowed inside and outside of functions. Inside those functions, just like variables, the nested functions are local and therefore cannot be obtained from the outside scope.

function foo() {
    function bar() {
        return 1;
    }
    return bar();
}

foo manipulates bar within itself. bar cannot be touched from the outer scope unless it is defined in the outer scope.

So this will not work:

function foo() {
    function bar() {
        return 1;
    }
}

bar(); // throws error: bar is not defined
David G
  • 94,763
  • 41
  • 167
  • 253
4

When you declare a function within a function, the inner functions are only available in the scope in which they are declared, or in your case, the pad2 can only be called in the dmy scope.

All the variables existing in dmy are visible in pad2, but it doesn't happen the other way around :D

pedrochaves
  • 782
  • 6
  • 20
3

It's perfectly normal in JavaScript (and many languages) to have functions inside functions.

Take the time to learn the language, don't use it on the basis that it's similar to what you already know. I'd suggest watching Douglas Crockford's series of YUI presentations on JavaScript, with special focus on Act III: Function the Ultimate (link to video download, slides, and transcript)

Federico klez Culloca
  • 26,308
  • 17
  • 56
  • 95
Joaquim Rendeiro
  • 1,388
  • 8
  • 13
0

function foo() {
  function bar() {
    return 1;
  }
}
bar();

Will throw an error. Since bar is defined inside foo, bar will only be accessible inside foo.
To use bar you need to run it inside foo.

function foo() {
  function bar() {
    return 1;
  }
  bar();
}
Justin Liu
  • 581
  • 6
  • 21
0

Nested functions can be the basis for writing a modular group of related functions, kind of halfway to full object-oriented programming (static classes only).

Here is an example of such a group of functions, in this case to convert a value to a JSON string or a JSON string to a value.

Notice how the inner functions are grouped into an Object inside an outer function, and how the Object is then stored into a group name. This is the only name directly visible from outside the group. To reach any contained function from outside, you just write the group name, a period, then the function name. To reach a contained function from inside, you can use the same notation, or 'this', a period, then the function name.

//--------------------------------------------------------------------//
//      Module J:
//          Convert from and to JSON strings
//--------------------------------------------------------------------//
const J=NewJ();
function NewJ()
    {
    const mod=
        {
        From:(str)=>
            {
            return JSON.parse(str);
            }, // From
        To:(val)=>
            {
            return JSON.stringify(val,null,3);
            } // To
        }; // mod
    return mod;
    } // NewJ

//--------------------------------------------------------------------//
//      End Module J
//--------------------------------------------------------------------//

Here's a test:

console.log(J.To({A:'a'}));

Console output:

{
   "A": "a"
}
David Spector
  • 1,520
  • 15
  • 21