JavaScript: functions
By Dmitry Kabanov
As any programming language, JavaScript allows to pack useful things into functions, and it is the key to good JavaScript knowledge, to use them efficiently. In this post, I describe three ways that the functions can be declared in JavaScript, talk about visibility of variables inside the functions, how to have optional function arguments with default values, how to create closures (functions, bound to outer variables), and how to use recursion.
Defining a function using the keyword function
One can declare a function value like this (note the semicolon in the end):
const f = function(a) {
return a * a;
};
One can do some kind of polymorphism by redefining a function this way:
// Here we assume that there is a global variable COND1 that determines the
// function that we will use.
let g = function(a) {
return a + 2;
};
if (COND1 > 5) {
g = function(a) {
return 3 * a;
};
}
Therefore, the behavior of function g
will be different in different contexts.
The more common way of defining a function is to combine the keyword function
together with a name:
function add(a, b) {
return a + b;
}
Note that in this case, one does not need to use a semicolon after the function.
Variables and scopes
Each binding, that is, a variable, has a scope, that is, it can be visible only in a particular part of a program. If a variable defined outside of any functions and braces, it is a global variable.
If a variable defined inside a function, it is a local variable.
Note, that in the context of a function, the difference between keywords
var
and let
becomes clear.
If a variable declared using the keyword var
inside a function,
then it is visible everywhere in this function.
Starting with 2015, one can define a variable using the let
keyword.
Such variables are visible only inside a block where they are declared.
For example, in the following code:
function sum_to_n(n) {
let sum = 0;
for (let i = 0; i < n; i++) {
sum += i;
}
}
the sum
variable is visible in the whole function, while i
variable
is visible only inside the for
loop.
Arrow functions
There is also a concise way of defining an anonymous function in ES6 (2015):
const my_func = (x, y) => x * y;
Here, the arguments are in parentheses, and the return value is after =>
characters.
If there is only one argument, then parentheses can be omitted:
const square = x => x * x;
One of the important details about arrow functions is that
in simple cases, no return
keyword should be used.
If the function body is surrounded by braces, then the return
keyword
must be used:
const sq1 = x => x * x;
const sq2 = x => { return x * x; };
Note that if the function does not accept any parameters, then the parameter list is just a pair of parentheses:
const hello_world = () => {
console.log("Hello World!");
};
Optional arguments
In JavaScript, the number of arguments when invoking a function does not
have to be the same as defined in function definition.
All extra arguments will be ignored, while the missing arguments are assigned
the value undefined
:
function func(a, b) {
console.log(`a = ${a}`);
console.log(`b = ${b}`);
}
func(3, 5.25, "this will be ignored");
func(3);
a = 3
b = 5.25
a = 3
b = undefined
Because the value undefined
is usually not very useful, you can provide
the desired default value after the equality sign:
function func_with_optional_args(a, b = 2) {
console.log(`a = ${a}`);
console.log(`b = ${b}`);
}
func_with_optional_args(3, 5.25, "this will be ignored");
func_with_optional_args(3);
a = 3
b = 5.25
a = 3
b = 2
Note that the third arguments will be ignored anyway.
Closures
JavaScript also has closures, that is, functions that access variables from the enclosing scope.
It is a useful feature to create parameterized functions. For example, we can create a function that multiply a given value by an arbitrary argument:
function multiplier(factor) {
return n => factor * n;
}
times_2 = multiplier(2);
times_5 = multiplier(5);
console.log(times_2(10));
console.log(times_5(10));
20
50
Recursion
Recursive function is a function that calls itself. There must be a termination condition in such a function to make sure that it stops calling itself infinitely many times.
Let’s write a function that computes a Fibonacci number via recursion.
function fibonacci(n) {
if (n <= 0) {
return 0;
}
if (n == 1) {
return 1;
}
return fibonacci(n-1) + fibonacci(n-2);
}
console.log("7th Fibonacci number is", fibonacci(7));
7th Fibonacci number is 13
Of course, recursion is not the most effective way to solve this problem. Usually, recursion works well in the problem that require branching and checking multiple options.
References
This post is mostly based on Chapter 3 of the fantastic book on Javascript “Eloquent JavaScript” by Marijn Haverbeke. I have also consulted Mozilla Developer Network documentation a bit.