Nested functions (or inner functions) in JavaScript are a useful feature. They are functions inside another function. Learn how to create a nested function. How variable scopes, closures & lexical scopes work. Also learn to create multi-level nested function, passing parameters to them etc
Table of Contents
Nested Function
A nested function is a function inside another function
We can create the nested function in the same way we create the normal JavaScript function, But inside another function
In the following example, we create the logToConsole
function inside the addNum
function. We can invoke logToConsole
just like any other function, but only inside the addNum
function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | function addNum(a,b) { //nested function function logToConsole(message) { console.log(message); } let result=a+b; //invoking the nested function logToConsole("result is "+result) } addNum(1,2) ***output *** result is 3 |
Variable Scope in Nested Function
The nested functions have their own scope. But they also have access to the parent functions scope. Hence you need to remember two important points.
- A nested function is private to containing function
- A nested function can access the containing function’s scope
Nested function is private to containing function
Only the containing function can access the nested function. We cannot access it anywhere outside the function. This is because the inner function is defined in the scope of the outer function (or containing function).
In the example below, we try to access the logToConsole from the outside of the addNum function. But It will result in an error.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | function addNum(a,b) { function print(message) { console.log(message); } let result=a+b; logToConsole("result is "+result) } logToConsole("10") //Uncaught ReferenceError: logToConsole is not defined |
Nested function can access the containing functions scope
The nested function can access all variables, functions & arguments defined in the containing function. This ability where the inner function can access the parent function scope is known as Lexical scope.
In the example, we do not pass any value to logToConsole
. Inside the nested function, we can access both arguments of the containing function i.e. a
& b
, and also the result
variable.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | function addNum(a,b) { function logToConsole() { console.log(`result of %d + %d is %d`,a ,b,result); } let result=a+b; logToConsole() } addNum(1,2) |
Returning a Nested Function
The container function can return the nested function. In the following example, the makeCounter
initializes the count
property and returns the increment
function. We store the inner function in the counter
property
Whenever we invoke the counter
it will increment the count and return the result. What makes it interesting here is that the increment
function can still access the count
property of the makeCounter
, although the makeCounter
function finished its execution. This works because of a JavaScript feature known as Closure
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | function makeCounter() { let count = 0; increment = function () { return ++count; }; return increment; } //storing the inner or nested function counter = makeCounter() //Invoking the nested function. This will increment the count property by 1 console.log(counter()); console.log(counter()); console.log(counter()); *** Result *** 1 2 3 |
Closure
The count property is local to the makeCounter function. The increment function can access it because it is nested inside the makeCounter function.
In this line of code, we invoke the makeCounter
, which initializes the count
to 0 and returns the increment
function.
1 2 3 | counter = makeCounter() |
Usually, we expect JavaScript to clean up the memory when the function returns. But in the example above, we return the inner function which needs to access the count property of makeCounter
function. Cleaning up the memory will make the count property inaccessible to the inner function.
In such cases, JavaScript keeps the memory alive and attaches it to the inner function. This is called closure. The JavaScript keeps this in memory as long as someone has reference to the increment
function.
Hence, when we invoke the increment function using the counter variable, it will increment the count property. Because the count property is not destroyed and still in memory.
1 2 3 4 5 | console.log(counter()); console.log(counter()); console.log(counter()); |
In this example, we invoke makeCounter twice. Each call to makeCounter will get its own memory and count property. Because of this each of returned nested functions gets its own copy of the count property. Hence they do not interfere with each other.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | function makeCounter() { let count = 0; increment = function () { return ++count; }; return increment; } counter1= makeCounter() counter2= makeCounter() console.log(counter1()); //1 console.log(counter1()); //2 console.log(counter1()); //3 console.log(counter2()); //1 console.log(counter2()); //2 console.log(counter2()); //3 |
Returning a new object with a nested function
In the following example, the function does not contain a nested function but returns an object, which contains a function. Technically the function inside the object is also a nested function. It can also access the properties of its parent function i.e. makeCounter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | function makeCounter(counterName) { let count = 0; let counterObj = { name: counterName, increment: function () { ++count; console.log("name %s count %d",this.name,count) return count } } return counterObj } counter1=makeCounter("Counter1") counter2=makeCounter("Counter2") counter1.increment() counter2.increment() counter2.increment() counter1.increment() counter1.increment() counter2.increment() |
Parameters in nested function
You can also pass arguments to inner function. In the following example, outerfunc returns the innerFunc. Both accepts a parameters
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | function outerfunc(a) { innerFunc = function (b) { console.log("a %d b %d",a,b) }; return innerFunc; } //Get Inner Function InnerFunc=outerfunc(5); //Invoke it InnerFunc(3); //Invoke invoke inner Function directly outerfunc(10)(2); |
The code InnerFunc=outerfunc(5)
returns the inner function. You can invoke it as nnerFunc(3)
Another way to invoke the inner function is using the outerfunc(10)(2)
Mulitple levels of Nesting
We can also create multiple levels of nested functions in JavaScript. The following code is a 3 level nested function.
outerFunc
contains a functionInnerFunc
, which itself contains a functioninnermostFunc
.outerFunc
can invokeinnerFunc
. But cannot invokeinnermostFunc
innerFunc
can access the properties & methods ofouterFunc
innermostFunc
can accessinnerFunc
. It can also access theouterFunc
The Important point to note here is that innermostFunc
can access the outerFunc
. This is because the scopes are recursive. The innermostFunc
contains the scope of the innerFunc
. The innerFunc
contains the scope of outerFunc
. This is called scope chaining.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | function outerFunc(a) { innerFunc = function (b) { innermostFunc = function (c) { console.log("a %d b %d c %d", a, b, c) } return innermostFunc; }; return innerFunc; } //Invoke it outerFunc(10)(2)(7); //a 10 b 2 c 7 //Another way a = outerFunc(10) b = a(2) c = b(7) //a 10 b 2 c 7 |
Name conflicts
We run into name conflict when two functions define the same variable. For Example in the following example innerFunc
defines variable a
. Hence it will hide the a
variable of the outerfunc
. Hence the innermostFunc
will also see the variable from the innerFunc
because that is its immediate parent.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | function outerfunc(a) { innerFunc = function (b) { a=5 innermostFunc = function (c) { console.log("a %d b %d c %d", a, b, c) } return innermostFunc; }; return innerFunc; } //Invoke it outerfunc(10)(2)(7); //a 5 b 2 c 7 //Another way a = outerfunc(10) b = a(2) c = b(7) //a 5 b 2 c 7 |
In no-2 program’s line no 5 the function name should be “logToConsole” as declared in the previous program