Functions are values that can be called. One way of defining a function is called a function declaration.

The return statement returns a value from id. You can call a function by mentioning its name, followed by arguments in parentheses:

  1. > id('hello')
  2. 'hello'

If you don’t return anything from a function, undefined is returned (implicitly):

  1. > function f() { }
  2. > f()
  3. undefined

This section showed just one way of defining and one way of calling a function. Others are described later.

The Three Roles of Functions in JavaScript

Once you have defined a function as just shown, it can play several roles:

  • Nonmethod function (“normal function”)
  • You can call a function directly. Then it works as a normal function. Here’s an example invocation:
  1. id('hello')

By convention, the names of normal functions start with lowercase letters.

  • Constructor
  • You can invoke a function via the new operator. Then it becomes a constructor, a factory for objects. Here’s an example invocation:
  1. new Date()

By convention, the names of constructors start with uppercase letters.

  • Method
  • You can store a function in a property of an object, which turns it into a method that you can invoke via that object. Here’s an example invocation:
  1. obj.method()

By convention, the names of methods start with lowercase letters.

Nonmethod functions are explained in this chapter; constructors and methods are explained in .

Terminology: “Parameter” Versus “Argument”

The terms parameter and argument are

  • Parameters are used to define a function. They are also called formal parameters and formal arguments. In the following example, param1 and param2 are parameters:
  1. function foo(param1, param2) {
  2. ...
  3. }
  • Arguments are used to invoke a function. They are also called actual parameters and actual arguments. In the following example, 3 and 7 are arguments:
  1. foo(3, 7);

This section describes three ways to create a function:

  • Via a function expression
  • Via a function declaration
  • Via the constructor Function()

All functions are objects, instances of Function:

  1. function id(x) {
  2. return x;
  3. }
  4. console.log(id instanceof Function); // true

Therefore, functions get their methods from Function.prototype.

A function expression produces a value—a function object. For example:

  1. var add = function (x, y) { return x + y };
  2. console.log(add(2, 3)); // 5

The preceding code assigned the result of a function expression to the variable add and called it via that variable. The value produced by a function expression can be assigned to a variable (as shown in the last example), passed as an argument to another function, and more. Because normal function expressions don’t have a name, they are also called anonymous function expressions.

Named function expressions

You can give a function expression a name. Named function expressions allow a function expression to refer to itself, which is useful for self-recursion:

  1. var fac = function me(n) {
  2. if (n > 0) {
  3. return n * me(n-1);
  4. } else {
  5. return 1;
  6. }
  7. };
  8. console.log(fac(3)); // 6

Note

The name of a named function expression is only accessible inside the function expression:

  1. var repeat = function me(n, str) {
  2. return n > 0 ? str + me(n-1, str) : '';
  3. };
  4. console.log(repeat(3, 'Yeah')); // YeahYeahYeah
  5. console.log(me); // ReferenceError: me is not defined

Function Declarations

The following is a function declaration:

  1. function add(x, y) {
  2. return x + y;
  3. }

The preceding looks like a function expression, but it is a statement (see ). It is roughly equivalent to the following code:

  1. return x + y;
  2. };

In other words, a function declaration declares a new variable, creates a function object, and assigns it to the variable.

The Function Constructor

The constructor Function() evaluates JavaScript code stored in strings. For example, the following code is equivalent to the previous example:

  1. var add = new Function('x', 'y', 'return x + y');

However, this way of defining a function is slow and keeps code in strings (inaccessible to tools). Therefore, it is much better to use a function expression or a function declaration if possible. explains Function() in more detail; it works similarly to eval().

Hoisting

Hoisting means “moving to the beginning of a scope.” Function declarations are hoisted completely, variable declarations only partially.

Function declarations are completely hoisted. That allows you to call a function before it has been declared:

  1. foo();
  2. function foo() { // this function is hoisted
  3. ...
  4. }

The reason the preceding code works is that JavaScript engines move the declaration of foo to the beginning of the scope. They execute the code as if it looked like this:

  1. function foo() {
  2. ...
  3. }
  4. foo();

var declarations are hoisted, too, but only the declarations, not assignments made with them. Therefore, using a var declaration and a function expression similarly to the previous example results in an error:

  1. foo(); // TypeError: undefined is not a function
  2. var foo = function () {
  3. };

Only the variable declaration is hoisted. The engine executes the preceding code as:

The Name of a Function

  1. > function f1() {}
  2. > f1.name
  3. 'f1'

The name of anonymous function expressions

  1. > var f2 = function () {};
  2. > f2.name
  3. ''

Named function expressions, however, do have a name:

  1. > var f3 = function myName() {};
  2. > f3.name
  3. 'myName'

The name of a function is useful for debugging. Some people always give their function expressions names for that reason.

Should you prefer a

  1. function id(x) {
  2. return x;
  3. }

Or the equivalent combination of a var declaration plus a function expression?

  1. var id = function (x) {
  2. return x;
  3. };

They are basically the same, but function declarations have two advantages over function expressions:

  • They are hoisted (see ), so you can call them before they appear in the source code.
  • They have a name (see The Name of a Function). However, JavaScript engines are getting better at inferring the names of anonymous function expressions.

More Control over Function Calls: call(), apply(), and bind()

call(), apply(), and bind() are methods that all functions have (remember that functions are objects and therefore have methods). They can supply a value for this when invoking a method and thus are mainly interesting in an object-oriented context (see ). This section explains two use cases for nonmethods.

func.apply(thisValue, argArray)

This method uses the elements of argArray as arguments while calling the function func; that is, the following two expressions are equivalent:

  1. func(arg1, arg2, arg3)
  2. func.apply(null, [arg1, arg2, arg3])

thisValue is the value that this has while executing func. It is not needed in a non-object-oriented setting and is thus null here.

apply() is useful whenever a function accepts multiple arguments in an array-like manner, but not an array.

Thanks to apply(), we can use Math.max() (see ) to determine the maximum element of an array:

  1. > Math.max(17, 33, 2)
  2. 33
  3. > Math.max.apply(null, [17, 33, 2])
  4. 33

This performs partial function application—a new function is created that calls func with this set to thisValue and the following arguments: first arg1 until argN, and then the actual arguments of the new function.thisValue is not needed in the following non-object-oriented setting, which is why it is null.

Here, we use bind() to create a new function plus1() that is like add(), but only requires the parameter y, because x is always 1:

  1. function add(x, y) {
  2. return x + y;
  3. }
  4. var plus1 = add.bind(null, 1);
  5. console.log(plus1(5)); // 6

In other words, we have created a new function that is equivalent to the following code:

  1. function plus1(y) {
  2. return add(1, y);
  3. }

Handling Missing or Extra Parameters

JavaScript does

  • More actual parameters than formal parameters
  • The extra parameters are ignored but can be retrieved via the special array-like variable arguments (discussed momentarily).
  • Fewer actual parameters than formal parameters
  • The missing formal parameters all have the value undefined.

All Parameters by Index: The Special Variable arguments

The special variable arguments exists only inside functions (including methods). It is an array-like object that holds all of the actual parameters of the current function call. The following code uses it:

  1. function logArgs() {
  2. for (var i=0; i<arguments.length; i++) {
  3. console.log(i+'. '+arguments[i]);
  4. }
  5. }

And here is the interaction:

  1. > logArgs('hello', 'world')
  2. 0. hello
  3. 1. world

arguments has the following characteristics:

  • It is array-like, but not an array. On one hand, it has a property length, and individual parameters can be read and written by index.

On the other hand, arguments is not an array, it is only similar to one. It has none of the array methods (slice(), forEach(), etc.). Thankfully, you can borrow array methods or convert arguments to an array, as explained in .

  • It is an object, so all object methods and operators are available. For example, you can use the in operator (Iteration and Detection of Properties) to check whether arguments “has” a given index:
  1. > function f() { return 1 in arguments }
  2. > f('a')
  3. false
  4. > f('a', 'b')
  5. true

You can use hasOwnProperty() () in a similar manner:

  1. > function g() { return arguments.hasOwnProperty(1) }
  2. > g('a', 'b')
  3. true

Deprecated features of arguments

Strict mode drops several of the more unusual features of arguments:

  • arguments.callee refers to the current function. It is mainly used to do self-recursion in anonymous functions, and is not allowed in strict mode. As a workaround, use a named function expression (see ), which can refer to itself via its name.
  • In nonstrict mode, arguments stays up-to-date if you change a parameter:
  1. function sloppyFunc(param) {
  2. return arguments[0];
  3. }
  4. console.log(sloppyFunc('value')); // changed

But this kind of updating is not done in strict mode:

  1. function strictFunc(param) {
  2. 'use strict';
  3. param = 'changed';
  4. return arguments[0];
  5. }
  6. console.log(strictFunc('value')); // value
  • Strict mode forbids assigning to the variable arguments (e.g., via arguments++). Assigning to elements and properties is still allowed.

Mandatory Parameters, Enforcing a Minimum Arity

There are three First, you can check if it is undefined:

  1. function foo(mandatory, optional) {
  2. if (mandatory === undefined) {
  3. throw new Error('Missing parameter: mandatory');
  4. }
  5. }

Second, you can interpret the parameter as a boolean. Then undefined is considered false. However, there is a caveat: several other values are also considered (see ), so the check cannot distinguish between, say, 0 and a missing parameter:

  1. if (!mandatory) {
  2. throw new Error('Missing parameter: mandatory');
  3. }

Third, you can also check the length of arguments to enforce a minimum arity:

The last approach differs from the other ones:

  • The first two approaches don’t distinguish between foo() and foo(undefined). In both cases, an exception is thrown.
  • The third approach throws an exception for foo() and sets optional to undefined for foo(undefined).

Optional Parameters

If a parameter is optional, it means that you give it a default value if it is missing.

  1. function bar(arg1, arg2, optional) {
  2. if (optional === undefined) {
  3. optional = 'default value';
  4. }
  5. }

Second, interpret optional as a boolean:

  1. if (!optional) {
  2. optional = 'default value';
  3. }

Third, you can use the Or operator || (see ), which returns the left operand, if it isn’t falsy. Otherwise, it returns the right operand:

  1. // Or operator: use left operand if it isn't falsy
  2. optional = optional || 'default value';

Fourth, you can check a function’s arity via arguments.length:

  1. if (arguments.length < 3) {
  2. optional = 'default value';
  3. }

Again, the last approach differs from the other ones:

  • The first three approaches don’t distinguish between bar(1, 2) and bar(1, 2, undefined). In both cases, optional is 'default value'.
  • The fourth approach sets optional to 'default value' for bar(1, 2) and leaves it undefined (i.e., unchanged) for bar(1, 2, undefined).

Another possibility is to hand in optional parameters as named parameters, as properties of an object literal (see ).

Simulating Pass-by-Reference Parameters

In JavaScript, you cannot pass parameters by reference; that is, if you pass a variable to a function, its value is copied and handed to the function (pass by value). Therefore, the function can’t change the variable. If you need to do so, you must wrap the value of the variable (e.g., in an array).

This example demonstates a function that increments a variable:

  1. function incRef(numberRef) {
  2. numberRef[0]++;
  3. }
  4. var n = [7];
  5. incRef(n);
  6. console.log(n[0]); // 8

If you hand a function c as a parameter to another function f, then you have to be aware of two signatures:

  • The signature that f expects its parameter to have. f might provide several parameters, and c can decide how many (if any) of them to use.
  • The actual signature of c. For example, it might support optional parameters.

If the two diverge, then you can get unexpected results: c could have optional parameters that you don’t know about and that would interpret additional arguments provided by f incorrectly.

As an example, consider the array method map() (see ) whose parameter is normally a function with a single parameter:

  1. > [ 1, 2, 3 ].map(function (x) { return x * x })
  2. [ 1, 4, 9 ]

One function that you could pass as an argument is parseInt() (see ):

  1. > parseInt('1024')
  2. 1024

You may (incorrectly) think that map() provides only a single argument and that parseInt() accepts only a single argument. Then you would be surprised by the following result:

  1. > [ '1', '2', '3' ].map(parseInt)
  2. [ 1, NaN, NaN ]

map() expects a function with the following signature:

  1. function (element, index, array)

But parseInt() has the following signature:

  1. parseInt(string, radix?)

Thus, map() not only fills in string (via element), but also radix (via index). That means that the values of the preceding array are produced as follows:

  1. > parseInt('1', 0)
  2. 1
  3. > parseInt('2', 1)
  4. NaN
  5. > parseInt('3', 2)
  6. NaN

To sum up, be careful with functions and methods whose signature you are not sure about. If you use them, it often makes sense to be explicit about what parameters are received and what parameters are passed on. That is achieved via a callback:

  1. > ['1', '2', '3'].map(function (x) { return parseInt(x, 10) })
  2. [ 1, 2, 3 ]

When calling a

  • Positional parameters are mapped by position. The first actual parameter is mapped to the first formal parameter, the second actual to the second formal, and so on.
  • Named parameters use names (labels) to perform the mapping. Names are associated with formal parameters in a function definition and label actual parameters in a function call. It does not matter in which order named parameters appear, as long as they are correctly labeled.

Named parameters have two main benefits: they provide descriptions for arguments in function calls and they work well for optional parameters. I’ll first explain the benefits and then show you how to simulate named parameters in JavaScript via object literals.

Named Parameters as Descriptions

As soon as a function

  1. selectEntries(3, 20, 2);

what do these three numbers mean? Python supports named parameters, and they make it easy to figure out what is going on:

  1. selectEntries(start=3, end=20, step=2) # Python syntax

Optional Named Parameters

Optional positional parameters work well only if they are omitted at the end. Anywhere else, you have to insert placeholders such as null so that the remaining parameters have correct positions.With optional named parameters, that is not an issue. You can easily omit any of them.Here are some examples:

  1. # Python syntax
  2. selectEntries(step=2)
  3. selectEntries(end=20, start=3)
  4. selectEntries()

Simulating Named Parameters in JavaScript

JavaScript does not have native support for named parameters like Python and many other languages.

  1. selectEntries({ start: 3, end: 20, step: 2 });

The function receives an object with the properties start, end, and step. You can omit any of them:

  1. selectEntries({ step: 2 });
  2. selectEntries({ end: 20, start: 3 });
  3. selectEntries();

You could implement selectEntries() as follows:

You can also combine positional parameters with named parameters. It is customary for the latter to come last:

    Note

    In JavaScript, the pattern for named parameters shown here is sometimes called options or option object (e.g., by the jQuery documentation).