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
:
function
id
(
x
)
{
return
x
;
}
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() undefined
This 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:
new
Date
()
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:
function
foo
(
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
:
function
id
(
x
)
{
return
x
;
}
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:
var
add
=
function
(
x
,
y
)
{
return
x
+
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:
var
fac
=
function
me
(
n
)
{
if
(
n
>
0
)
{
return
n
*
me
(
n
-
1
);
}
else
{
return
1
;
}
};
console
.
log
(
fac
(
3
));
// 6
The name of a named function expression is only accessible inside the function expression:
var
repeat
=
function
me
(
n
,
str
)
{
return
n
>
0
?
str
+
me
(
n
-
1
,
str
)
:
''
;
};
console
.
log
(
repeat
(
3
,
'Yeah'
));
// YeahYeahYeah
console
.
log
(
me
);
// ReferenceError: me is not defined
The following is a function declaration:
function
add
(
x
,
y
)
{
return
x
+
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:
var
add
=
function
(
x
,
y
)
{
return
x
+
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:
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. 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
();
function
foo
()
{
// 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:
function
foo
()
{
...
}
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 function
var
foo
=
function
()
{
...
};
Only the variable declaration is hoisted. The engine executes the preceding code as:
var
foo
;
foo
();
// TypeError: undefined is not a function
foo
=
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?
function
id
(
x
)
{
return
x
;
}
Or the equivalent combination of a var
declaration plus a function expression?
var
id
=
function
(
x
)
{
return
x
;
};
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:
function
add
(
x
,
y
)
{
return
x
+
y
;
}
var
plus1
=
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:
function
plus1
(
y
)
{
return
add
(
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:
function
logArgs
()
{
for
(
var
i
=
0
;
i
<
arguments
.
length
;
i
++
)
{
console
.
log
(
i
+
'. '
+
arguments
[
i
]);
}
}
And here is the interaction:
> logArgs('hello', 'world') 0. hello 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 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') true
You can use hasOwnProperty()
(Iteration and Detection of Properties) in a similar manner:
> function g() { return arguments.hasOwnProperty(1) } > g('a', 'b') true
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 Named function expressions), which can refer to itself via its name.
In nonstrict mode, arguments
stays up-to-date if you change a parameter:
function
sloppyFunc
(
param
)
{
param
=
'changed'
;
return
arguments
[
0
];
}
console
.
log
(
sloppyFunc
(
'value'
));
// changed
But this kind of updating is not done in strict mode:
function
strictFunc
(
param
)
{
'use strict'
;
param
=
'changed'
;
return
arguments
[
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
:
function
foo
(
mandatory
,
optional
)
{
if
(
mandatory
===
undefined
)
{
throw
new
Error
(
'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
)
{
throw
new
Error
(
'Missing parameter: mandatory'
);
}
Third, you can also check the length of arguments
to enforce a minimum arity:
if
(
arguments
.
length
<
1
)
{
throw
new
Error
(
'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
:
function
bar
(
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 falsy
optional
=
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:
function
incRef
(
numberRef
)
{
numberRef
[
0
]
++
;
}
var
n
=
[
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') 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', '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) 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', '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 syntax
selectEntries
(
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:
function
selectEntries
(
options
)
{
options
=
options
||
{};
var
start
=
options
.
start
||
0
;
var
end
=
options
.
end
||
getDbLength
();
var
step
=
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
});