12 KiB
Functions
Quite often we need to perform a similar action in many places of the script.
For example, we need to show a nice-looking message when a visitor logs in, logs out and maybe somewhere else.
Functions are the main "building blocks" of the program. They allow the code to be called many times without repetition.
[cut]
We've already seen examples of built-in functions, like alert(message)
, prompt(message, default)
and confirm(question)
. But we can create functions of our own as well.
Definition
An example of a function definition:
function showMessage() {
alert( 'Hello everyone!' );
}
The function
keyword goes first, then follows the name of the function, then a list of parameters in the brackets (empty in the example above) and finally the code of the function, also named "the function body".

Once defined, the function can be called by it's name.
For instance:
//+ run
function showMessage() {
alert( 'Hello everyone!' );
}
*!*
showMessage();
showMessage();
*/!*
The call executes the code of the function. Here we will see the message shown two times.
In this example we can see one of the main purposes of the functions: to evade code duplication.
If we ever need to change the message or the way it is shown -- it's enough to modify the code in one place: the function which outputs it.
Local variables
A variable declared inside a function is only visible inside that function.
For example:
//+ run
function showMessage() {
*!*
let message = "Hello, I'm JavaScript!"; // local variable
*/!*
alert( message );
}
showMessage(); // Hello, I'm JavaScript!
alert( message ); // <-- Error! The variable is local to the function
Outer variables
A function can access an outer variable as well, for example:
//+ run no-beautify
let *!*userName*/!* = 'John';
function showMessage() {
let message = 'Hello, my name is ' + *!*userName*/!*;
alert(message);
}
showMessage(); // Hello, my name is John
The function can not only read but also modify an outer variable.
For instance:
//+ run
let *!*userName*/!* = 'John';
function showMessage() {
userName = "Bob"; // (1) changed the outer variable
let message = 'Hello, my name is ' + *!*userName*/!*;
alert(message);
}
alert( userName ); // John before the function call
showMessage();
*!*
alert( userName ); // Bob, the value was modified by the function
*/!*
If we had let
before userName
in the line (1) then the function would have a local variable userName
and use it instead of the outer one:
//+ run
let userName = 'John';
function showMessage() {
*!*
let userName = "Bob"; // declare a local variable
*/!*
let message = 'Hello, my name is ' + userName;
alert(message);
}
// the function will create and use it's own userName
showMessage();
*!*
alert( userName ); // John, unmodified
*/!*
Global variables
Variables declared on the script level, outside of any function, are called global.
Global variables are visible from any function. They are used to store the data of a project-wide importance. Variables needed by specific tasks should reside in the corresponding functions.
Without strict mode, for compatibility with the old scripts, it is possible to create a variable by an assignment, without a declaration.
Consider the code below as an example. The variable message
becomes global.
//+ run no-strict
function showMessage() {
message = 'Hello'; // (*) assignment without declaration!
}
showMessage();
alert( message ); // Hello
With "use strict"
there will be an error in line (*)
. Without it, there won't be.
In the code above, most probably, the programmer simply forgot to write let
, so the error message is a good thing. A one more reason to "use strict"
all the time.
Modern editors and tools for code quality checking like jshint allow to see and fix "missed declarations" early while coding.
Parameters
We can pass arbitrary data to the function using it's parameters (also called arguments) .
For example, this code shows two messages:
//+ run no-beautify
function showMessage(*!*from, text*/!*) { // arguments: from, text
from = "**" + from + "**";
alert(from + ': ' + text);
}
*!*
showMessage('Ann', 'Hello!'); // **Ann**: Hello!
showMessage('Ann', "What's up?"); // **Ann**: What's up?
*/!*
In the example above, the function has two parameters: from
and text
.
When the function is called, the values in the brackets are copied to local variables from
and next
.
Note that the function can modify those local variables freely, they are not seen from outside anyway:
//+ run
function showMessage(from, text) {
*!*
from = '**' + from + '**'; // changes the local from
*/!*
alert( from + ': ' + text );
}
let from = "Ann";
showMessage(from, "Hello");
// the old value of "from" is still here, the function modified a local copy
alert( from ); // Ann
Default parameter values
A function can be called with any number of arguments. If a parameter is not provided, but listed in the declaration, then its value becomes undefined
.
For instance, a function showMessage(from, text)
can actually be called with a single argument:
showMessage("Ann");
Normally, such call would output "Ann: undefined"
, because text === undefined
.
But we can modify the function to detect missed parameter and assign a "default value" to it:
//+ run
function showMessage(from, text) {
*!*
if (text === undefined) {
text = 'no text given';
}
*/!*
alert( from + ": " + text );
}
showMessage("Ann", "Hello!"); // Ann: Hello!
*!*
showMessage("Ann"); // Ann: no text given
*/!*
Optional arguments are usually given at the end of the list.
There are three most used ways to assign default values:
- We can check if the argument equals `undefined`, and if yes then assign a default value to it. That's demonstrated by the example above.
- Use operator `||`:
function showMessage(from, text) { text = text || 'no text given'; ... }
This way is shorter, but the argument is considered missing also if it's falsy, like an empty line,
0
ornull
. - ES-2015 introduced an neater syntax for default values:
//+ run function showMessage(from, *!*text = 'no text given'*/!*) { alert( from + ": " + text ); } showMessage("Ann"); // Ann: no text for you
Here
'no text given'
is a string, but it can be any other value or expression, which is only evaluated and assigned if the parameter is missing.This syntax is not yet widely supported in the browsers, but available with the help of transpilers like Babel.
Returning a value
A function can return a value into the calling code as the result.
For instance, let's make a mathematical function calcD
to calculate a discriminant of a quadratic polynomial using the formula b2 - 4ac
:
//+ run no-beautify
function calcD(a, b, c) {
*!*return*/!* b*b - 4*a*c;
}
// discriminant for: -4x^2 + 2x + 1
let test = calcD(-4, 2, 1);
alert(test); // 20
The directive return
can be in any place of the function. When the execution reaches it, the function stops, and the value is returned to the calling code (assigned to test
above).
There may be many returns, for instance:
//+ run
function checkAge(age) {
if (age > 18) {
return true;
} else {
return confirm('Got a permission from the parents?');
}
}
let age = prompt('How old are you?', 18);
if ( checkAge(age) ) {
alert( 'Access granted' );
} else {
alert( 'Access denied' );
}
The return
can be used without a value, to exit from the function immediately.
For example:
function showMovie(age) {
if ( !checkAge(age) ) {
*!*
return;
*/!*
}
alert( "Showing you the movie" ); // (*)
// ...
}
In the code above, if checkAge(age)
returns false
, then showMovie
won't proceed to the alert
.
[smart header="A result with an empty or absent return
returns undefined
"]
If a function does not return a value, it is the same as if it returns undefined
:
//+ run
function doNothing() { /* empty */ }
alert( doNothing() ); // undefined
A return
with no argument is also the same as return undefined
:
//+ run
function doNothing() {
return;
}
alert( doNothing() === undefined ); // true
[/smart]
Naming a function [#function-naming]
Functions are actions. So their name is usually a verb.
Usually, function names have verbal prefixes which vaguely describe the action.
There is an agreement within the team on the terms here. For instance, functions that start with "show"
-- usually show something:
//+ no-beautify
showMessage(..) // shows a message
Function starting with...
- `"get"` -- allow to get something,
- `"calc"` -- calculate something,
- `"create"` -- create something,
- `"check"` -- check something and return a boolean, etc.
Examples of such names:
//+ no-beautify
getAge(..) // return the age (get it somehow)
calcD(..) // calculate a discriminant and return the result
createForm(..) // create a form, usually returns it
checkPermission(..) // check a permission, return true/false
That's very convenient. The prefix itself is a great hint. A glance on a function name gives an understanding what it does and what kind of value it returns.
[smart header="One function -- one action"] A function should do exactly what is suggested by its name.
Two independant actions usually deserve two functions, even if they are usually called together (in that case we can make a 3rd function calling those two).
Few examples of "shouldn't do" for the names listed above:
- `getAge` -- should not change `age`, also it should not show anything to the visitor.
- `calcD` -- should not store a calculated discriminant "for future reuse" anywhere. It should only calculate and return it.
- `createForm` -- should not show the form to the user or modify something in the document. It should only create it and return.
- `checkPermission` -- should only perform the check and return the result. It should not display the `access granted/denied` message.
[smart header="Ultrashort function names"] Functions that are used very often sometimes have ultrashort names.
For example, jQuery framework defines a function $
, LoDash library has it's core function named _
.
[/smart]
Summary
A function declaration looks like this:
function name(parameters, delimited, by, comma) {
/* code */
}
- Values passed in a function call become its local variables.
- A function can declare local variables.
- A function can return a value. If it doesn't then its result is `undefined`.
It is possible for a function to access variables defined outside of it.
But to make the code cleaner and easier to understand, it's recommended to use local variables and parameters instead as much as possible.
It is always easier to understand a function which gets parameters, works with them and returns a result than a function which gets no parameters, but modifies outer variables as a side-effect.
Function naming:
- A name should clearly describe what the function does. When we see a function call in the code, a good name instantly gives us an understanding what it does and returns.
- A function is an action, so function names are usually verbal.
- There is a bunch of commonly adapted verbal prefixes like `create…`, `show…`, `get…`, `check…` etc which can help. The main point is to be consistent about their meaning.
Functions are the main building blocks of scripts. Now we covered the basics, so we actually can use them.
But we are going to return to them, going more deeply in their advanced features.