A JavaScript Function is a block of JavaScript code that performs a task or calculates a value. A function has a name and it usually takes in some input and returns an output. Functions are reusable and can be executed as many times as necessary.
Functions, as reusable blocks of code, are fully supported in JavaScript. Here is an example of a simple function declaration:
function say(name) {
console.log("Hello " + name);
}
say("Nico"); // => Hello Nico
The above function declaration starts with the keyword function
. Next we
have say
, which is the function's name. Following the name is a comma-separated
list of zero or more parameters enclosed by parentheses: ()
. In our example we
have one parameter named name
. These parameters behave like local variables
inside the function body. The function body comprises a set of JavaScript statements
enclosed by curly braces: {}
. The body executes when the function is
invoked by issueing its name followed by parenthesis (with possible arguments),
like say("Nico");
.
Function declarations can appear in the top-level JavaScript program itself,
just like our say
example. However, functions can also be nested
within other functions, like so.
function hypotenuse(m, n) { // outer function
function square(num) { // inner function
return num * num;
}
return Math.sqrt(square(m) + square(n));
}
console.log(hypotenuse(3, 4)); // => 5
The outer function hypotenuse
contains an inner function square
.
The square
function is visible only within the hypotenuse
function
body, that is, square
has function scope only. The easiest way
to look at square
is as a private helper function to the outer function.
The examples before are function declarations. However, JavaScript also supports function expressions in which functions are assigned to variables. Here is an example:
var area = function (radius) {
return Math.PI * radius * radius;
};
console.log(area(5)); // => 78.5398..
This function is assigned to a variable named area
. The function itself does
not have a name, so this is an anonymous function. Function expressions are
invoked by calling the variable name with a pair of parentheses containing any arguments
(in our example: area(5));
.
How do we distinguish a function declaration from a function expression? A ny time a function is a part of an assignment, it is treated as a function expression. Function declarations do not end with a semicolon, whereas function expressions do, as they are part of an assignment. Also, function declarations must always have a name whereas a function expression may or may not have a name, i.e. it can be named or anonymous.
Anonymous functions are commonly used in JavaScript. Our area
function is an example
of this. By the way: if you choose to give the function expression a name it is
generally recommended you give it the same name as the variable it is assigned to;
this will avoid confusion.
A JavaScript function can be invoked before its declaration. This works because the JavaScript
engine implicitly hoists the function to the top so that they are visible throughout the program.
In the example below, the function named course
is parsed and evaluated before any
other code is run.
function course() { // hoisted to top of program
return "Learning JS";
}
console.log(course()); // => Learning JS
A variable has global scope if it exists during the life of the program and is accessible from anywhere in the program. A variable has function scope if it is declared within a function and is not accessible outside of that function and will cease to exist when the function finishes execution.
With function scope, the parameters and variables that you define as part of a function are not available outside the function body; they are only visible within the function throughout the lifetime of the function. Here are some examples:
var num = 1; // variable is global
function showGlobal() {
console.log(num); // uses the global num
}
showGlobal(); // => 1
function showLocal() {
var num = 2; // num is local, hides the global num
console.log(num);
}
showLocal(); // => 2
function showArgument(num) {
console.log(num); // arguments are locally scoped
}
showArgument(3); // => 3
Unfortunately JavaScript does not support block-level scoping. A variable defined within a block, for instance an if- or a for-statement, will exist even after the block finishes execution. It will be available until the function in which the variable is declared finishes.
function noBlock() {
if (true) {
var width = 10; // not block level scoped
}
console.log(width); // variable num is available outside the if block
}
noBlock(); // => 10
JavaScript functions can be nested within other functions. A nested (inner) function can access the arguments and variables of the (outer) function it is nested within. The example below demonstrates this:
function verify(name) { // outer function
function isJohn() { // inner function
return name === "John"; // full access to argument
}
if (isJohn()) {
console.log("Yep, this is John");
}
}
verify("John");
The inner function isJohn
has full access to the name argument of the outer
function; there is no need to pass a parameter to the inner function.
What happens when a function has a property and its prototype has a property of the same name? Which one will be used? Let's run a test and find out:
function Counter(count) {
this.count = count; // object property
}
Counter.prototype.count = 2; // prototype property
var counter = new Counter(6);
console.log(counter.count); // => 6
This code demonstrates object properties take precedence over (i.e. hide) prototype properties of the same name.
Closures are an important and powerful concept in JavaScript. Simply put, a closure represents the variables of a function that are kept alive, even after the function returns. In almost all other languages when a function returns all local variables are destroyed and the program moves on, but not in JavaScript.
Many JavaScript programmers are familiar with the concept of holding a reference to a function
with a variable (if not, see the discussion of function expressions before this section).
Many other languages have similar concepts where they can reference functions through
function pointers or delegates. However, in JavaScript when a variable holds a reference
to a function it also maintains a second, but hidden, reference to its closure, which
essentially are the local variables that were present at the time of creation and their
current values. These variables are its scope context. Let's show this with an example
(note that counter()
is a regular function and not a constructor function):
function counter() {
var index = 0;
function increment() {
index = index + 1;
console.log(index);
return index;
}
return increment;
}
var userIncrement = counter(); // a reference to inner increment()
var adminIncrement = counter(); // a reference to inner increment()
userIncrement(); // => 1
userIncrement(); // => 2
adminIncrement(); // => 1
adminIncrement(); // => 2
adminIncrement(); // => 3
When the counter
function is invoked, it returns a reference to the increment
function.
At the time counter
finishes execution, JavaScript saves its scope, and only the function
that it returns, in our example increment
, has access to it. This is the
function's closure and includes a copy of the variable index
, which is 0
following the initialization. Subsequent calls to increment
increments
the index
value. Note that userIncrement
and adminIncrement
each have their own closure with their own copy of the index
variable that
only they can work on.
Arguments are local to their functions, so they also become part of the closure context. Here is an example in which name is part of the closure.
function greetings(name) {
function sayWelcome() {
console.log("Welcome to " + name);
}
return sayWelcome;
}
var greet = greetings("New York");
greet(); // => Welcome to New York
greet(); // => Welcome to New York
greet(); // => Welcome to New York
Since all JavaScript functions are objects and they have a scope chain associated with them, they are, in fact, closures. Most commonly however, a closure is created when a nested function object is returned from the function within which it was defined.
The nested function closures are a powerful feature of JavaScript and they are commonly used in advanced applications. For instance, it is commonly used when declaring event callbacks. Many JavaScript libraries make use of closures. Our unique Dofactory JS demonstrates many advanced uses of JavaScript closures. To learn more click here.
Suppose you have a function that expects a large number of parameters, like this:
function createCompany(name, street, city, zip, state, type, taxId, currency, isActive, parentCompany) { ... }
It would be difficult for the client of the function to recall all the parameters and put them in the correct order. When you invoke a function in JavaScript with an incorrect number or order of arguments you are not going to get an error. It just makes the missing variables undefined. So, it is easy to introduce bugs when calling functions that accepts a large number of parameters.
An nice solution to this kind of scenario is called the Options Hash pattern. Essentially, it is a function that accepts a single parameter which is an object that encapsulates all the parameters:
var parms = {
name: "Levis",
street: "1 Main Street",
city: "Anyhwere",
zip: "01010",
state: "NY",
type: "Garments",
taxid: "983233-003",
currency: "USD",
isActive: true
};
function createCompany(parms) {
var name = parms.name;
var street = parms.street;
var city = parms.city;
console.log("State: " + parms.state);
console.log("Currency: " + parms.currency);
// ...
}
createCompany(parms);
The variable parms
is a simple object literal. It allows the parameters
to be added in any order. Also, optional parameters, such as parentCompany
in the
earlier code with the long parameter list, can be safely omitted. It is far easier to
maintain a parameter object than a list of individual parameters and worry about passing
them in correctly. By the way, this pattern also allows you to pass in more complex types,
such as callback functions.
Our Dofactory JS has more to say about the Option Hash pattern, including a wonderfully elegant way to handle default and missing values. To learn more click here.
Immediate functions execute as soon as JavaScript encounters them. These functions are a powerful concept that is frequently used in JavaScript. They are also referred to as self-invoking functions. First let's look at a normal function declaration. Clearly, it does not execute until it's invoked.
function print() {
console.log('Learning JS!');
}
print(); // executes print function
JavaScript allows you to execute a function immediately by enclosing it with parentheses followed by another pair of parentheses, like so:
(function () {
console.log("Learning JS!");
}());
The opening parenthesis before a function keyword tells the JavaScript to interpret the function keyword as a function expression. Without it, the interpreter treats it as a function declaration. The paired brackets () at the end is the argument list; in this case an empty list.
The example above is an anonymous function, that is, it has no name. There is no need for a name, because nowhere in the program will this function be called. It is called once, as soon as JavaScript encounters it.
You can also pass parameters to an immediate function.
(function square(value) {
var result = value * value;
console.log(result);
})(10);
Any variable you define in an immediate function, including the arguments passed, remains local to the function. By using these self-invoking functions, your global namespace won't be polluted with temporary variables.
Immediate functions offer great flexibility and are widely used in the most popular JavaScript frameworks, such as jQuery. Our accompanying Dofactory JS includes a great section on immediate functions and how JavaScript experts are using them to build robust and maintainable web apps. To learn more click here.
Next, we will look at an area where immediate functions can be useful: page initialization.
Immediate functions are useful for initialization tasks that are needed only once. A good example is when a web page is loaded in which you initialize objects, assign event handlers, etc. Using an immediate function allows you to package and run this process in the local scope of the function without leaving a global trace behind; only local variables are created and no global variables are left behind. The example below simply displays a welcome message with today's day.
(function () {
var days = ["Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"];
var today = new Date();
var message = "Welcome, today is " + days[today.getDay()];
console.log(message); // => Welcome, today is Monday
}());
This function executes once and there will be no trace left. If the above code were not wrapped
in an immediate function, the variables days
, today
, and message
would all end up on the global namespace, increasing the risk of name collisions. With the
immediate function we have zero footprint on the global namespace.
Functions in JavaScript have several built-in methods, two of which we will examine here;
call()
and apply()
. This may sound strange, but remember that a
function in JavaScript is an object so it has its own properties and its own methods.
The methods call()
and apply()
allow you to invoke the function,
giving you a lot of control over how the function executes. Consider the following code:
function message() {
console.log(this.num);
}
message(); // => undefined, 'this' references global object
var obj = { num: 2 };
message.call(obj); // => 2, 'this' references obj
message.apply(obj); // => 2, 'this' references obj
The functions call
and apply
are very similar: the first argument
passed is the same, which is the object on which the function is to be invoked. With this
argument you essentially control what this
is bound to inside the function body.
This is a powerful concept, because what this implies is that any function can be invoked
as a method of any object, even when it is not actually a method of that object.
If you are familiar with .NET extension methods, then you'll immediately understand the concept.
An optional second argument to call()
is a number of arguments to be passed to the function
that is invoked. In apply()
the optional second argument is the same, but specified
as an array of arbitrary length. When you know the exact number of arguments use call()
,
otherwise use apply()
.
Here is another example of both methods in action.
var welcome = function (guest) {
console.log("I'm " + this.name + ", you're " + guest.name);
};
var stan = { name: "Stan" };
var laurel = { name: "Laurel" };
welcome.call(stan, laurel); // => I'm Stan, you're Laurel
welcome.call(laurel, stan); // => I'm Laurel, you're Stan
welcome.apply(stan, [laurel]); // => I'm Stan, you're Laurel
welcome.apply(laurel, [stan]); // => I'm Laurel, you're Stan
The first call has stan
bound to this
and laurel
as
the guest
. In the second call the roles are reversed. The apply
methods
are the same except that the guest
argument is passed as an array.
For a broader discussion on these powerful functions and additional invocation patterns check out our unique Dofactory JS product. To learn more click here.