10 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.
Function Declaration
To create a function we can use a function declaration.
It looks like this:
function showMessage() {
alert( 'Hello everyone!' );
}
The function
keyword goes first, then goes 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".
Our new function can be called by it's name.
For instance:
function showMessage() {
alert( 'Hello everyone!' );
}
*!*
showMessage();
showMessage();
*/!*
The call showMessage()
executes the code of the function. Here we will see the message two times.
This example clearly demonstrates 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:
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:
let *!*userName*/!* = 'John';
function showMessage() {
let message = 'Hello, ' + *!*userName*/!*;
alert(message);
}
showMessage(); // Hello, my name is John
The function has a full access to an outer variable. It can modify it as well.
For instance:
let *!*userName*/!* = 'John';
function showMessage() {
*!*userName*/!* = "Bob"; // (1) changed the outer variable
let message = 'Hello, ' + *!*userName*/!*;
alert(message);
}
alert( userName ); // *!*John*/!* before the function call
showMessage();
alert( userName ); // *!*Bob*/!*, the value was modified by the function
Sometimes that happens when we forget let
. Because the outer variable is only used if there's no local one.
For example, if we had let
before userName
in the line (1) then the function would have a local variable userName
and use it instead. This process is called "shadowing".
In the code below the local userName
shadows the outer one:
let userName = 'John';
function showMessage() {
*!*
let userName = "Bob"; // declare a local variable
*/!*
let message = 'Hello, ' + userName; // *!*Bob*/!*
alert(message);
}
// the function will create and use it's own userName
showMessage();
alert( userName ); // *!*John*/!*, unchanged, the function did not access the outer variable
Variables declared outside of any function, such as the outer `userName` in the code above, are called *global*.
Global variables are visible from any function (unless shadowed by locals).
Usually, a function declares all variables specific to its task, and global variables only store the data so important that it really must be seen from anywhere. Modern code has little to no globals, most variables reside in their functions.
Parameters
We can pass arbitrary data to function using it's parameters (also called function arguments) .
In the example below, the function has two parameters: from
and text
.
function showMessage(*!*from, text*/!*) { // arguments: from, text
alert(from + ': ' + text);
}
*!*
showMessage('Ann', 'Hello!'); // Ann: Hello!
showMessage('Ann', "What's up?"); // Ann: What's up?
*/!*
When the function is called, the values in the brackets are copied to local variables from
and next
.
Please note that because the function can modify them. The changes are made to copies, so they won't affect anything outside:
function showMessage(from, text) {
*!*
from = '*' + from + '*'; // make "from" look nicer
*/!*
alert( from + ': ' + text );
}
let from = "Ann";
showMessage(from, "Hello"); // *Ann*: Hello
// the value of "from" is the same, the function modified a local copy
alert( from ); // Ann
Returning a value
A function can return a value back into the calling code as the result.
The simplest example would be a function that sums two values:
function sum(a, b) {
*!*return*/!* a + b;
}
let result = sum(1, 2);
alert( result ); // 3
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 result
above).
There may be many occurences of return
in a single function. For instance:
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' );
}
It is possible to use return
without a value. That causes the function to exit 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 function with an empty return
or without it returns undefined
"
If a function does not return a value, it is the same as if it returns undefined
:
function doNothing() { /* empty */ }
alert( doNothing() === undefined ); // true
An empty return
is also the same as return undefined
:
function doNothing() {
return;
}
alert( doNothing() === undefined ); // true
## Naming a function [#function-naming]
Functions are actions. So their name is usually a verb. It should briefly, but as accurately as possible describe what the function does. So that a person who reads the code gets the right clue.
It is a widespread practice to start a function with a verbal prefix which vaguely describes the action. There must be an agreement within the team on the meaning of the prefixes.
For instance, functions that start with `"show"` -- usually show something.
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:
```js no-beautify
showMessage(..) // shows a message
getAge(..) // returns the age (gets it somehow)
calcSum(..) // calculates a sum and returns the result
createForm(..) // creates a form (and usually returns it)
checkPermission(..) // checks a permission, returns true/false
```
With prefixes at place, a glance at a function name gives an understanding what kind of work 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, no more.
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 breaking this rule:
- `getAge` -- would be bad if it shows an `alert` with the age (should only get).
- `createForm` -- would be bad if it modifies the document, adding a form to it (should only create it and return).
- `checkPermission` -- would be bad if displays the `access granted/denied` message (should only perform the check and return the result).
These examples reflect few common meanings of prefixes, the final word comes from you and your team. Maybe it's pretty normal for your code to behave differently. But you should to have a firm understanding what a prefix means, what a prefixed function can and what it can not do. All same-prefixed functions should obey the rules. And the team should share the knowledge.
```
```smart header="Ultrashort function names"
Functions that are used *very often* sometimes have ultrashort names.
For example, [jQuery](http://jquery.com) framework defines a function `$`, [LoDash](http://lodash.com/) library has it's core function named `_`.
These are exceptions. Generally functions names should be concise, but descriptive.
```
## Summary
A function declaration looks like this:
```js
function name(parameters, delimited, by, comma) {
/* code */
}
```
- Values passed to function as parameters are copied to its local variables.
- A function may access outer variables. But it works only one-way. The code outside of the function doesn't see its 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 start creating and using them. But that's only the beginning of the path. We are going to return to them many times, going more deeply in their advanced features.