One of the most powerful parts of JavaScript is the function. Functions serve as a way of breaking programs into smaller repeatable parts. Functions have three parts themselves: name, arguments, and body. There are two phases of working with functions, declaration and call.
Function fundamentals
Let’s look at a function declaration.
// Name: addOne
// Arguments: number
function addOne( number ) {
// Body: return number + 1;
return number + 1;
}
We named the function addOne, because in the body we are taking our argument number
, and returning the value of it plus one. After a function is declared we can call it like so:
function addOne( number ) {
return number + 1;
}
// Outputs 3 to the console.
console.log( addOne( 2 ) );
When we call a function, we supply the needed arguments, and the body of the function is evaluated, and run. The body is like a mini program inside of our program we can also use functions previously declared inside of other functions.
function addOne( number ) {
return number + 1;
}
function three() {
return addOne(2);
}
// Outputs 3 to the console.
console.log( three() );
We declared two functions above, addOne and three. Three pulls in addOne from the upper scope and calls it passing in a 2
. We get 3
as the return value. This ability to use functions we declare inside of other functions is very powerful as it allows us to build programs out of small repeatable patterns.
JavaScript allows us to create functions without names, which can be useful for a number of reasons.
Anonymous functions
Anonymous functions are useful when your particular function you create does not need to be used across your program in multiple parts, they serve as sort of “one use” mini programs. An anonymous function might look like this:
function( message ) {
console.log( message );
}
There isn’t very much special about this at first glance it is just a function declaration without a name. There is not even a way to call this function, so what’s so interesting about anonymous functions? Well they are still a declared value like this:
var log = function( message ) {
console.log( message );
}
// Outputs "Hello!"
log( "Hello!" );
So we can have alternate ways of “naming” functions, it is important to note that the function does not have a name it is just bound to a variable that we named log
. This next example is going to be a little weird and might take a bit to understand:
// Outputs "Calling immediately!" tot he console.
( function ( message ) {
console.log( message );
} ( "Calling immediately!" ) );
This is known as an immediately invoked function expression or IIFE for short. It is definitely a little strange. We wrap the entire thing in a set of parentheses to mark the expression. We declare an anonymous function, and then immediately call it with "Calling immediately!"
. It is weird, but it is an important aspect of understanding JavaScript. The reason this works is because in JavaScript functions are values.
Functions as values
Values can be passed around and used anywhere an expression can be used in JavaScript, expressions as you remember from part 2 are pieces of code that should evaluate to some value. It can get a bit weird thinking about a function as a value as it is a bit abstract. It is much more easy to thing of values like 1, 2, 3, and “Strings”. Let’s look at some cool things that come from functions as values:
function Tim() {
console.log( "Hi Tim!" );
}
function Xi() {
console.log( "Nǐ hǎo" );
}
function Emilie() {
console.log( "Bonjour Emilie!" );
}
function greet( greetFunction ) {
greetFunction();
}
// Outputs "Hi Tim!"
greet( Tim );
// Outputs "Xi"
greet( Xi );
// Outputs "Bonjour Emilie!"
greet( Emelie );
We have three greeting functions for three different people. We then create a generic function greet
which takes a function as its only argument, and calls whatever the function is. So we create one greet
function that can handle and call another function at any point we choose. Pretty neat.
Scope
JavaScript has different types of scoping to it, but at it its core it is mainly a functionally scoped language. What is that exactly? Understanding scope is best achieved through looking at examples:
var myVarInUpperScope = 1;
var myVar = 2;
// Always will output 2 in this scope.
console.log( myVar );
function coolFunction( myVar ) {
// Will output whatever the argument is that is passed to this function, creating a new scope.
console.log( myVar );
// Always returns 1, because there is no other variable named in this scope, so JavaScript looks to the higher scope.
return myVarInUpperScope;
}
// Outputs 3, returns 1.
coolFunction( 3 );
// myVar is still 3
console.log( myVar );
// Outputs 1, and returns 1.
coolFunction( myVarInUpperScope );
We can also declare new variables inside a function that will only be accessible to that scope, or other scopes within that function let’s look at a more complex example:
var upperScope = 'Upper';
// Always will output 'Upper' in this scope, unless reassigned later.
console.log( upperScope );
function changeUpperScope() {
// Grabs the upperScope variable and changes its value.
upperScope = 'Reassigned!';
}
function defineNewUpperScope() {
// Make a new variable declaration inside this function scope.
var upperScope = 'Function';
var functionScope = 'FunctionScope';
return upperScope;
}
// Outputs 'Upper', since that is the currently declared value.
console.log( upperScope );
// Returns 'Function', from within the functions scope.
defineNewUpperScope();
// Even though upperScope was defined inside the previous function it
// does not affect the variable at the global scope.
// Still outputs 'Upper'
console.log( upperScope );
// Now the global scope will get reassigned from within a function.
// It grabs the reference from the upperscope and reassigns it.
// Make note of how changeUpperScope does not use a var declaration.
// Instead it uses the same reference as the upperscope, watch what
// happens.
// Call our reassigning function.
changeUpperScope();
// Outputs 'Reassigned!'
console.log( upperScope );
Scope can be complicated in JavaScript, so experimenting with it and really getting a strong grasp on it is critical to understanding JavaScript. We will see how powerful functional scoping can be as well as how catastrophic it can be if not used properly. A common scope problem that can arise is a collision.
Avoiding collisions
JavaScript is a dynamically typed language. This is a fancy way of saying that any value or reference can essentially be overwritten at any point. Any variable, or object we find, can be rewritten on the fly and be something completely different. This crazyness is what often turns people away from using dynamic languages, on highly critical applications, because it is much easier to introduce bugs into dynamic programs. Let’s look at a very common example; collisions. In JavaScript you end up using a lot of other people’s code. Here is Dinesh’s write function.
function write() {
document.write( 'I am Dinesh' );
}
write();
Here is Arjana’s write function.
function write() {
document.write( 'I am Arjana' );
}
write();
Both functions are written into the global scope, so if we loaded both of them at the same time in our project, the later loading function would override the other and become the actual function. Resulting that " I am Arjana"
is written twice. We can avoid collisions by leveraging IIFE’s like we saw before. Here are what Dinesh’s and Arjana’s files should look like to prevent collisions.
// Dinesh
( function () {
function write() {
document.write( 'I am Dinesh' );
}
write();
} () );
// Arjana
( function () {
function write() {
document.write( 'I am Arjana' );
}
write();
} () );
In this case both write functions will now work, because they are independently scoped first. We create an anonymous function, which creates a new function scope, and place the code we want to run inside of it. We then call the function with ()
, and wrap the entire expression in another set of parentheses. This immediately calls the function we created, and executes the code inside the body of the function with it’s own scope.
We will look at collisions and dependencies in the future more, this is a rough example that probably does not illustrate the issue the best. This technique is known as using a function closure.
Closures
Closures are a special way to use functions to “close over” values and references into a particular scope. Let’s look at an interesting example:
function myClosure ( start ) {
return {
next: function() {
return start++;
}
}
}
const counter = myClosure( 1 );
// Returns 1
counter.next();
// Returns 2
counter.next();
// Returns 3
counter.next();
const newCounter = myClosure( 10 );
// Returns 10
newCounter.next();
// Now for something a little weirder.
const newerCounter = myClosure( counter.next() + newCoutner.next() );
// Returns 15
const newerCounter.next();
// Returns undefined, start only exists inside the closure.
console.log( start );
In the future we will see how the above example is actually quite useful. Let’s look at myClosure
and what exactly is going on. It is a function that takes an argument start. It returns an object, with a property next. Next’s value is an anonymous function, that returns the post increment value of start; meaning, we get the current value and then the number is incremented by one.
Inside of the next function the start
variable is being grabbed from the upperscope of myClosure
. Once we call myClosure
with a number, we can no longer access what number was passed in, we can only interact with that number by using next
; a very powerful property known as encapsulation. Encapsulation means that certain data can no longer be accessed from the outer scope, we “close over” those values. They can still be interacted with from the outer scope, but they do not exist in the outer scope. It definitely takes a bit to fully understand so don’t worry if it does not sink in immediately. We will look more in depth at this example coming up soon, and the different capabilities this provides.
Wrap Up
Functions serve as a powerful tool in JavaScript to allow us to build complex programs out of small reusable parts. We will be checking out some more advanced function features in JavaScript coming up next. If this did not fully set in, take time to reread and experiment on your own, if you get stuck leave something in the comments section.