JavaScript Functions

https://www.dofactory.com/javascript/functions

JavaScript Functions

Declaring JavaScript functions

Functions, as reusable blocks of code, are fully supported in JavaScript. Here is an example of a simple function declaration:

function say(name) {    alert("Hello " + name);}         say("Nico");              // => Hello Nico

Run

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));}alert(hypotenuse(3,4));            // => 5

Run

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.

Function expressions

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;};alert(area(5));         // => 78.5

Run

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? Any 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. Previous Next

Function hoisting

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.

alert(course());        // => Learning JSfunction course(){      // hoisted to top of program       return "Learning JS";}

Run

Variable scope in functions

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 globalfunction showGlobal() {  alert(num);         // uses the global num}showGlobal();         // => 1function showLocal(){  var num = 2;        // num is local, hides the global num   alert(num);}showLocal();           // => 2function showArgument(num) {    alert(num);       // arguments are locally scoped}showArgument(3);      // => 3

Run

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   }   alert(width);       // variable num is available outside the if block}noBlock ();            // => 10

Run

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()) {        alert("Yep, this is John");    }}verify("John");

Run

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.

Variable scoping and prototypes

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 propertyvar counter = new Counter(6);alert(counter.count);             // => 6

Run

This code demonstrates object properties take precedence over (i.e. hide) prototype properties of the same name.

What is a JavaScript closure?

Closure is 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;       alert(index);       return index;    }    return increment;}var userIncrement = counter();    // a reference to inner increment()var adminIncrement = counter();   // a reference to inner increment()userIncrement();                  // => 1userIncrement();                  // => 2adminIncrement();                 // => 1adminIncrement();                 // => 2adminIncrement();                 // => 3

Run

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.<%--

When counter is invoked, it returns a reference to the increment function which is assigned to the variable userIncrement. The increment function closes the context and preserves the value of the variable index, which is 0. This is the function's closure. When counter finishes its execution, JavaScript saves its scope, and only the function that it returns, in our example increment, has access to it. Note that userIncrement and adminIncrement each have their own closure with their own copy of index that only they can work on.

Although the increment function is executed outside of counter function, it can still access the variable index. This is possible because JavaScript supports lexical-scoping and closures. Lexical-scoping means that inner functions have access to variables in the outer function. Closure means that a function remembers its state that was in effect when it was defined (and beyond). Since increment is defined within counter, it has access to the index variable of the function counter, even if counter completed its execution.--%>

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() {       alert("Welcome to " + name);    }    return sayWelcome;}var greet = greetings("New York");greet();           // => Welcome to New Yorkgreet();           // => Welcome to New Yorkgreet();           // => Welcome to New York

Run

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 JavaScript + jQuery Design Pattern Framework demonstrates many advanced uses of JavaScript closures. To learn more click here.

Option Hash pattern

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;    alert("State: " + parms.state);    alert("Currency: " + parms.currency);    // ...}createCompany(parms);

Run

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 JavaScript + jQuery Design Pattern Framework 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

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() {    alert('Learning JS!');}print();          // executes print function

Run

JavaScript allows you to execute a function immediately by enclosing it with parentheses followed by another pair of parentheses, like so:

(function () {    alert("Learning JS!");}());

Run

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;    alert(result);})(10);

Run

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 JavaScript + jQuery Design Pattern Framework 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.

Web 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()];    alert(message);     // => Welcome, today is Monday}());

Run

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.

Invoking functions with call() and apply()

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() {    alert(this.num);}message();        // => undefined, 'this' references global objectvar obj = { num: 2 };message.call(obj);        // => 2,  'this' references objmessage.apply(obj);       // => 2,  'this' references obj

Run

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) {    alert("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 Laurelwelcome.call(laurel, stan);  // => I'm Laurel, you're Stanwelcome.apply(stan, [laurel]); // => I'm Stan, you're Laurelwelcome.apply(laurel, [stan]); // => I'm Laurel, you're Stan

Run

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 JavaScript + jQuery Design Pattern Framework. To learn more click here.

Last updated

Navigation

Lionel

@Copyright 2023