Functions are values that can be called. One way of defining a function is called a function declaration. For example, the following code defines the function id that has a single parameter, x:
functionid(x){returnx;}
The return statement returns a value from id. You can call a function by mentioning its name, followed by arguments in parentheses:
> id('hello')
'hello'If you don’t return anything from a function, undefined is returned (implicitly):
> function f() { }
> f()
undefinedThis section showed just one way of defining and one way of calling a function. Others are described later.
Once you have defined a function as just shown, it can play several roles:
You can call a function directly. Then it works as a normal function. Here’s an example invocation:
id('hello')
By convention, the names of normal functions start with lowercase letters.
You can invoke a function via the new operator. Then it becomes a constructor, a factory for objects. Here’s an example invocation:
newDate()
By convention, the names of constructors start with uppercase letters.
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:
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 Chapter 17.
The terms parameter and argument are often used interchangeably, because the context usually makes it clear what the intended meaning is. The following is a rule of thumb for distinguishing them.
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:
functionfoo(param1,param2){...}
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:
foo(3,7);
This section describes three ways to create a function:
Function()
All functions are objects, instances of Function:
functionid(x){returnx;}console.log(idinstanceofFunction);// true
Therefore, functions get their methods from Function.prototype.
A function expression produces a value—a function object. For example:
varadd=function(x,y){returnx+y};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.
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:
varfac=functionme(n){if(n>0){returnn*me(n-1);}else{return1;}};console.log(fac(3));// 6
The name of a named function expression is only accessible inside the function expression:
varrepeat=functionme(n,str){returnn>0?str+me(n-1,str):'';};console.log(repeat(3,'Yeah'));// YeahYeahYeahconsole.log(me);// ReferenceError: me is not defined
The following is a function declaration:
functionadd(x,y){returnx+y;}
The preceding looks like a function expression, but it is a statement (see Expressions Versus Statements). It is roughly equivalent to the following code:
varadd=function(x,y){returnx+y;};
In other words, a function declaration declares a new variable, creates a function object, and assigns it to the variable.
The constructor Function() evaluates JavaScript code stored in strings. For example, the following code is equivalent to the previous example:
varadd=newFunction('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. Evaluating Code Using new Function() explains Function() in more detail; it works similarly to eval().
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:
foo();functionfoo(){// this function is hoisted...}
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:
functionfoo(){...}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:
foo();// TypeError: undefined is not a functionvarfoo=function(){...};
Only the variable declaration is hoisted. The engine executes the preceding code as:
varfoo;foo();// TypeError: undefined is not a functionfoo=function(){...};
Most JavaScript engines support the nonstandard property name for function objects. Function declarations have it:
> function f1() {}
> f1.name
'f1'The name of anonymous function expressions is the empty string:
> var f2 = function () {};
> f2.name
''Named function expressions, however, do have a name:
> var f3 = function myName() {};
> f3.name
'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 function declaration like the following?
functionid(x){returnx;}
Or the equivalent combination of a var declaration plus a function expression?
varid=function(x){returnx;};
They are basically the same, but function declarations have two advantages over function expressions:
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 Calling Functions While Setting this: call(), apply(), and bind()). This section explains two use cases for nonmethods.
This method uses the elements of argArray as arguments while calling the function func; that is, the following two expressions are equivalent:
func(arg1,arg2,arg3)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 Other Functions) to determine the maximum element of an array:
> Math.max(17, 33, 2) 33 > Math.max.apply(null, [17, 33, 2]) 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:
functionadd(x,y){returnx+y;}varplus1=add.bind(null,1);console.log(plus1(5));// 6
In other words, we have created a new function that is equivalent to the following code:
functionplus1(y){returnadd(1,y);}
JavaScript does not enforce a function’s arity: you can call it with any number of actual parameters, independent of what formal parameters have been defined. Hence, the number of actual parameters and formal parameters can differ in two ways:
arguments (discussed momentarily).
undefined.
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:
functionlogArgs(){for(vari=0;i<arguments.length;i++){console.log(i+'. '+arguments[i]);}}
And here is the interaction:
> logArgs('hello', 'world')
0. hello
1. worldarguments 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 Array-Like Objects and Generic Methods.
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:
> function f() { return 1 in arguments }
> f('a')
false
> f('a', 'b')
trueYou can use hasOwnProperty() (Iteration and Detection of Properties) in a similar manner:
> function g() { return arguments.hasOwnProperty(1) }
> g('a', 'b')
trueStrict 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 Named function expressions), which can refer to itself via its name.
In nonstrict mode, arguments stays up-to-date if you change a parameter:
functionsloppyFunc(param){param='changed';returnarguments[0];}console.log(sloppyFunc('value'));// changed
But this kind of updating is not done in strict mode:
functionstrictFunc(param){'use strict';param='changed';returnarguments[0];}console.log(strictFunc('value'));// value
arguments (e.g., via arguments++). Assigning to elements and properties is still allowed.
There are three ways to find out whether a parameter is missing. First, you can check if it is undefined:
functionfoo(mandatory,optional){if(mandatory===undefined){thrownewError('Missing parameter: mandatory');}}
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 false (see Truthy and Falsy Values), so the check cannot distinguish between, say, 0 and a missing parameter:
if(!mandatory){thrownewError('Missing parameter: mandatory');}
Third, you can also check the length of arguments to enforce a minimum arity:
if(arguments.length<1){thrownewError('You need to provide at least 1 argument');}
The last approach differs from the other ones:
foo() and foo(undefined). In both cases, an exception is thrown.
foo() and sets optional to undefined for foo(undefined).
If a parameter is optional, it means that you give it a default value if it is missing. Similarly to mandatory parameters, there are four alternatives.
First, check for undefined:
functionbar(arg1,arg2,optional){if(optional===undefined){optional='default value';}}
Second, interpret optional as a boolean:
if(!optional){optional='default value';}
Third, you can use the Or operator || (see Logical Or (||)), which returns the left operand, if it isn’t falsy. Otherwise, it returns the right operand:
// Or operator: use left operand if it isn't falsyoptional=optional||'default value';
Fourth, you can check a function’s arity via arguments.length:
if(arguments.length<3){optional='default value';}
Again, the last approach differs from the other ones:
bar(1, 2) and bar(1, 2, undefined). In both cases, optional is 'default value'.
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 Named 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:
functionincRef(numberRef){numberRef[0]++;}varn=[7];incRef(n);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:
f expects its parameter to have. f might provide several parameters, and c can decide how many (if any) of them to use.
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 Transformation Methods) whose parameter is normally a function with a single parameter:
> [ 1, 2, 3 ].map(function (x) { return x * x })
[ 1, 4, 9 ]One function that you could pass as an argument is parseInt() (see Integers via parseInt()):
> parseInt('1024')
1024You 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', '2', '3' ].map(parseInt) [ 1, NaN, NaN ]
map() expects a function with the following signature:
function(element,index,array)
But parseInt() has the following signature:
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:
> parseInt('1', 0)
1
> parseInt('2', 1)
NaN
> parseInt('3', 2)
NaNTo 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', '2', '3'].map(function (x) { return parseInt(x, 10) })
[ 1, 2, 3 ]When calling a function (or method) in a programming language, you must map the actual parameters (specified by the caller) to the formal parameters (of a function definition). There are two common ways to do so:
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.
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:
selectEntries(start=3,end=20,step=2)# Python syntax
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:
# Python syntaxselectEntries(step=2)selectEntries(end=20,start=3)selectEntries()
JavaScript does not have native support for named parameters like Python and many other languages. But there is a reasonably elegant simulation: name parameters via an object literal, passed as a single actual parameter. When you use this technique, an invocation of selectEntries() looks like:
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:
selectEntries({step:2});selectEntries({end:20,start:3});selectEntries();
You could implement selectEntries() as follows:
functionselectEntries(options){options=options||{};varstart=options.start||0;varend=options.end||getDbLength();varstep=options.step||1;...}
You can also combine positional parameters with named parameters. It is customary for the latter to come last:
someFunc(posArg1,posArg2,{namedArg1:7,namedArg2:true});