Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Learning jQuery - Fourth Edition

You're reading from   Learning jQuery - Fourth Edition Add to your current website development skills with this brilliant guide to JQuery. This step by step course needs little prior JavaScript knowledge so is suitable for beginners and more seasoned developers alike.

Arrow left icon
Product type Paperback
Published in Jun 2013
Publisher Packt
ISBN-13 9781782163145
Length 444 pages
Edition 4th Edition
Languages
Tools
Arrow right icon
Toc

Table of Contents (24) Chapters Close

Learning jQuery Fourth Edition
Credits
Foreword
About the Authors
About the Reviewers
www.PacktPub.com
Preface
1. Getting Started FREE CHAPTER 2. Selecting Elements 3. Handling Events 4. Styling and Animating 5. Manipulating the DOM 6. Sending Data with Ajax 7. Using Plugins 8. Developing Plugins 9. Advanced Selectors and Traversing 10. Advanced Events 11. Advanced Effects 12. Advanced DOM Manipulation 13. Advanced Ajax JavaScript Closures Testing JavaScript with QUnit Quick Reference Index

Creating inner functions


JavaScript is fortunate to number itself among the programming languages that support inner function declarations. Many traditional programming languages, such as C, collect all functions in a single top-level scope. Languages with inner functions, on the other hand, allow us to gather small utility functions where they are needed, avoiding namespace pollution.

An inner function is simply a function that is defined inside of another function. For example:

function outerFn() {
  function innerFn() {
  }
}

Listing A.1

Here, innerFn() is an inner function, contained within the scope of outerFn(). This means that a call to innerFn() is valid within outerFn(), but not outside of it. The following code results in a JavaScript error:

function outerFn() {
  console.log('Outer function');
  function innerFn() {
    console.log('Inner Function');
  }
}
console.log('innerFn():');
innerFn();

Listing A.2

We can successfully run the code, though, by calling innerFn() from within outerFn():

function outerFn() {
  console.log('Outer function');
  function innerFn() {
    console.log('Inner function');
  }
  innerFn();
}
console.log('outerFn():');
outerFn();

Listing A.3

This results in the following output:

outerFn():
Outer function
Inner function

This technique is especially handy for small, single-purpose functions. For example, algorithms that are recursive, but have a nonrecursive API wrapper, are often best expressed with an inner function as a helper.

Calling inner functions from elsewhere

The plot thickens when function references come into play. Some languages, such as Pascal, allow the use of inner functions for the purpose of code hiding only; those functions are forever entombed within their parent functions. JavaScript, on the other hand, allows us to pass functions around just as if they were any other kind of data. This means inner functions can escape their captors.

The escape route can wind in many different directions. For example, suppose the function is assigned to a global variable:

var globalVar;

function outerFn() {
  console.log('Outer function');
  function innerFn() {
    console.log('Inner function');
  }
  globalVar = innerFn;
}
console.log('outerFn():');
outerFn();
console.log('globalVar():');
globalVar();

Listing A.4

The call to outerFn() after the function definition modifies the global variable globalVar. It is now a reference to innerFn(). This means that the later call to globalVar() operates just as an inner call to innerFn() would, and the print statements are reached:

outerFn():
Outer function
globalVar():
Inner function

Note that a call to innerFn() from outside of outerFn() still results in an error. Though the function has escaped by way of the reference stored in the global variable, the function name is still trapped inside the scope of outerFn().

A function reference can also find its way out of a parent function through a return value:

function outerFn() {
  console.log('Outer function');
  function innerFn() {
    console.log('Inner function');
  }
  return innerFn;
}
console.log('var fnRef = outerFn():');
var fnRef = outerFn();
console.log('fnRef():');
fnRef();

Listing A.5

Here, there is no global variable modified inside outerFn(). Instead, outerFn() returns a reference to innerFn(). The call to outerFn() results in this reference, which is stored and called itself in turn, triggering the message again:

var fnRef = outerFn():
Outer function
fnRef():
Inner function

The fact that inner functions can be invoked through a reference even after the function has gone out of scope means that JavaScript needs to keep referenced functions available as long as they could possibly be called. Each variable that refers to the function is tracked by the JavaScript runtime, and once the last has gone away, the JavaScript garbage collector comes along and frees up that bit of memory.

Understanding variable scope

Inner functions can of course have their own variables, which are restricted in scope to the function itself:

function outerFn() {
  function innerFn() {
    var innerVar = 0;
    innerVar++;
    console.log('innerVar = ' + innerVar);
  }
  return innerFn;
}
var fnRef = outerFn();
fnRef();
fnRef();
var fnRef2 = outerFn();
fnRef2();
fnRef2();

Listing A.6

Each time this inner function is called, through a reference or otherwise, a new variable innerVar is created, incremented, and displayed:

innerVar = 1
innerVar = 1
innerVar = 1
innerVar = 1

Inner functions can reference global variables in the same way as any other function can:

var globalVar = 0;
function outerFn() {
  function innerFn() {
    globalVar++;
    console.log('globalVar = ' + globalVar);
  }
  return innerFn;
}
var fnRef = outerFn();
fnRef();
fnRef();
var fnRef2 = outerFn();
fnRef2();
fnRef2();

Listing A.7

Now our function will consistently increment the variable with each call:

globalVar = 1
globalVar = 2
globalVar = 3
globalVar = 4

But what if the variable is local to the parent function? Since the inner function inherits its parent's scope, this variable can be referenced too:

function outerFn() {
  var outerVar = 0;
  function innerFn() {
    outerVar++;
    console.log('outerVar = ' + outerVar);
  }
  return innerFn;
}
var fnRef = outerFn();
fnRef();
fnRef();
var fnRef2 = outerFn();
fnRef2();
fnRef2();

Listing A.8

Now our function calls have more interesting behavior:

outerVar = 1
outerVar = 2
outerVar = 1
outerVar = 2

This time we get a mix of the two earlier effects. The calls to innerFn() through each reference increment outerVar independently. Note that the second call to outerFn() is not resetting the value of outerVar, but rather creating a new instance of outerVar bound to the scope of the second function call. The upshot of this is that after the previous calls, another call to fnRef() will print the value 3, and a subsequent call to fnRef2() will also print 3. The two counters are completely separate.

When a reference to an inner function finds its way outside of the scope in which the function was defined, this creates a closure on that function. We call variables that are neither parameters nor local to the inner function's free variables, and the environment of the outer function call closes them. Essentially, the fact that the function refers to a local variable in the outer function grants the variable a stay of execution. The memory is not released when the function completes, as it is still needed by the closure.

lock icon The rest of the chapter is locked
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime
Banner background image