Javascript functions
Overview
All programming functions have some input and output. The function contains instructions used to create the output from its input. Functions in JavaScript usually take in data, process it, and return a result. Once a function is written, it can be used over and over and over again. Functions are the foundation of the javascript language, and every Javascript developer should understand them. This tutorial explains what javascript functions are and their application.
What Are Javascript Functions?
JavaScript functions are a set of statements that perform a task or calculate a value. Values can be passed to a function as parameters, and the function will return a value.
Notable points :
- Functions are considered as one of the fundamental building blocks in Javascript.
- To use a function, it must be defined somewhere in the scope from which it will be called.
- A javascript function is composed of a sequence of statements called the function body.
- JavaScript function can take one or more inputs and can return output also. However, both taking input and returning an output are optional.
- Every function in JavaScript is a function object.
First Class Objects:
JavaScript Functions are called first-class objects because they can have properties and methods just like any other object. What distinguishes them from other objects is their ability to be called. So, they are also called function object.
Defining Functions
Function declarations:
A function declaration (also known as a function definition or function statement) consists of the function keyword, followed by:
- The function name
- The parameter list(enclosed in parentheses)
- The javascript statements that defines the function (enclosed in curly braces{})
For example:
Output:
In the above example, the function add takes two parameters -- a and b. The function consists of one statement, which is supposed to return the sum of the parameters(a and b) passed to the function. Then, the return a+b; statement specifies the value returned by this function.
The return statement is the last line of any function and ends function execution. It specifies a value to be returned to the calling function. However, it is not mandatory to include the return statement in every function.
For example, consider the below code:
Explanation: This is a function, which on being called alerts "Hello World!". We have not added any return statement in this code, because it is not required.
The following image shows the structure of a JavaScript function:
Explanation:
- We have a "function" keyword in JavaScript functions, followed by the "function name".
- There is a parameter list in the function that takes some input. The parameter list in any function is optional, for example:
Example:
The above function does not takes any input and has an empty parameter list. It may also be called a non-parameterized function. 3. There is a set of code statements enclosed in parentheses, also known as the function body. 4. We have an optional "return" statement that returns some output from the function.
Pass by value vs. Pass by Reference:
Functions in Pass by Value are called by directly passing the value of the variable as an argument. Thus, any changes made inside the function do not affect the original value. In simple words, the changes made to the arguments in pass-by-value are not reflected globally or in the calling function. In the above example, we passed a & b by value. They are also called primitive parameters.
In Pass by Reference, Functions are called bypassing the reference/address of the variable as an argument. So, altering the value of the parameters inside the function also changes the value globally(in the calling function). Passing objects as a parameter follows this property (here, object referred to as a non-primitive value, such as Array or a user-defined object).
Function expressions
A function expression is very similar to a function declaration. Their syntax, too, is almost similar. The main difference between a function expression and a function declaration is the function name, which can be omitted(not mandatory though) in function expressions to create an anonymous function. For example, the above add function can now be defined as --
Output:
Anonymous functions
Anonymous functions are the functions that do not have any name. Here, we use only the function keyword without the function name. We can basically call an anonymous function by assigning it to a variable and calling that variable.
Example:
Explanation: We declared a function without a function name and defined the function body like any other javascript function. We assigned this function to a variable so that we may call it. To call the anonymous function, we simply called x() followed by the parentheses, which we use to call any function.
Note: Functions are first-class citizens in JavaScript. Hence, they can be assigned to any variable, returned from any function or also passed as an argument to some function.
Arrow Functions
As of ES2015, we can also use arrow functions in place of function expressions. Arrow function expressions are somewhat a compact alternative to the normal function expressions; however, they are restricted and cannot be used for all situations.
- Demonstration of arrow functions:
Output:
Instead of writing "function" here, we simply passed the parameters followed by the equal to & greater than symbol => (representing an arrow). In the function statement, we simply returned the value. DOM-level methods like setTimeout, setInterval, addEventListener are great applications of the Arrow functions.
The above code can also be written as:
Explanation:
- Arrow functions provide us the flexibility to omit the parentheses {} (curly brackets) when we have a single line of code.
- If we have a single parameter, we can also omit the parentheses of the parameter list ().
- We can skip the return statement for a single line of function statement.
In simple words, if we have a single parameter, and a single line of code, then the parentheses and the return statements are implicitly assumed by javascript(or we may not mention them explicitly). So, in the above example, we have omitted the curly braces and the return keyword from the arrow function.
However, there are a few limitations to Arrow functions in JavaScript. For example, the arrow functions don't have their own this binding and are not suitable to be used for the call, apply and bind methods. They cannot be used as a constructor either. Read more of arrow functions limitations here in the MDN Docs
When are function expressions useful?
Function expressions are most useful when:
- We want to pass a function as an argument to another function.
- We want to use our functions as values.
As we know, JavaScript Functions are first-class citizens; we can obviously pass functions as arguments or assign them to a variable.
Example:
Output:
Explanation:
- In the first line of the code, we have a function expression that is assigned to a variable a, so that we may call it in the future.
- There is another function, function b, that expects a parameter(a function).
- Finally, we pass the variable a (which holds the function expression) to the function b. Inside b, we execute a() using the parentheses we use while calling functions.
Takeaway:
Function expressions are functions without a name assigned to a variable. So, they are termed as expressions. They can be called by using the variable name followed by () .
Calling Functions
If we just define a function, then it is of no use until we actually call it. Defining functions, just names the function and specifies what to do when the function is called.
Calling a function is when we actually call it by providing it the required parameters it is expecting.
To call a function, simply write the function name followed by the parameters enclosed in curly braces:
Example:
Some very important points to note about JavaScript function calling:
- All arguments in JavaScript functions are optional or loosely typed.
- JavaScript functions can be invoked with any number of arguments, irrespective of the number of arguments expected in the function definition.
- Since a function is loosely typed, we cannot declare the type of arguments it expects, hence we can pass any number of arguments to it.
- When a function is invoked with fewer arguments than that are declared, the additional arguments have the default value undefined.
Takeaway: We can call a function by writing the function name followed by parentheses, where we can pass the arguments list.
Calling a function without all the arguments
We can call a function with 0 or more arguments. By default, the parameters in functions are assigned to undefined. So, if we do not provide any default value to the parameters, undefined is returned.
Example:
Output:
Explanation: Here is a function myFunc() which is expecting 2 parameters a and b. We have called this function with 0 arguments; hence it is just logging their values as 'undefined'.
Takeaway: If you call a function without passing any argument, then the parameter list of the function is 'undefined'.
Setting default parameter values
Default function parameters in JavaScript allow parameters to be initialized with default values if no value or undefined is passed to them.
Default parameters were introduced as a part of ES2015.
Syntax:
Example of default parameters:
Output:
In JavaScript, function parameters have a default set to 'undefined'. So, it's often useful to set a different default value to prevent any unexpected result. Let's see what happens if we run the above code without setting default parameter values --
Without the default parameters:
Output:
In the following example, if no value is provided for a and b when add is called,a's and b's value would be undefined when evaluating a + b, and multiply would return NaN. To prevent this, it is preferable to set the default values.
Takeaway: We can provide default values to our parameters like func(a=0, b=0). It helps us to prevent unexpected results in our code.
In JavaScript, NaN stands for Not a Number. It represents a value that is not a valid number.
Calling a function with more arguments than parameters
In JavaScript, we can call a function with any number of arguments. The rest parameter syntax allows a function to accept an indefinite/variable number of arguments as an array.
A function definition's last parameter can be prefixed with "...anyName" , which will cause all remaining parameters passed by the user to be placed within a standard JavaScript array.
If you write ES6 compatible code, then the rest parameters should be preferred.
Syntax:
Example 1:
Output:
In this example, the paramters got the value in the order the arguments are passed. So, a got the 1^st^ value = 1, b the 2^nd^ value = 2, ...extraArgs got rest of the values = [3,4,5]
Rest Parameter(...)
As part of ES6, there is a new kind of parameter, the rest parameter, that starts with three dots (...). Following are the major features of Rest parameter:
-
Rest parameter allows us to represent any number of arguments(indefinite) as an array. Example:
Explanation: In the function demo(), the last parameter (args) is prefixed with the three-dots (...). It is called a rest parameter (...args).
-
Any number of arguments that we pass to the function demo() will be mapped(assigned) to the arguments list. Let's take an example: Example:
Explanation: In the above example, '5' will be mapped to 'x', '10' will be mapped to 'y'. And the rest of the arguments like 15, 'Hi', 24.5 will be mapped to the rest operator ...args. So, the args array will store the following values:
-
In case, you pass only the number of arguments, which is already expected in the parameter list, like demo(5, 10), then the rest parameter will be an empty array. Because,the first two arguments will be mapped to the parameters x, y.
The args array will be:
Let us also see some important points that we need to keep in mind before using the rest parameters.
Points to be noted:
-
A function definition can have only one ...restParameter.
myFunc(...restParam1, ...restParam2, ...restParam2,) // Incorrect myFunc(...restParam) //Correct
-
The rest parameter must be the last parameter in the function definition.
myFunc(...restParamters, arg1, arg2,) //Wrong order myFunc(arg1, arg2, ...restParamters) //Correct order
Getting into arguments with the arguments object
JavaScript functions have a built-in object called the arguments object. We may say, "arguments" is an Array-like object which can be accessed inside the functions. The argument object contains the values of the arguments(as an array) which we pass to the function. You can pass any variable number of arguments to a function and access it index-wise using arguments.
For example, if a function is passed 3 arguments, you can access them as follows:
Example:
Output:
So, as we see here, we can access all the values with the help of the argument object through indexes. We can even re-assign the values of argument objects. And, the arguments object is pass by value, so even if we change the value of any parameter in the function, it will not reflect the change in the original value of the parameter. Let's see how:
Example:
Output:
Here, the value of the argument is re-assigned to a new value. In the above example, the value of y is re-assigned in the function func1, but it did not changed the original value of y. So, it clearly shows that it is pass by value.
We can also access the regular parameters(which the function is already expecting) along with the argument objects. For example --
Output:
Here, we can access 'a' either ways, using 'a' directly or through the arguments object.
The arguments object is not an Array. It is similar, but lacks all Array properties except length. For example, it does not have the pop() method.
Key Takeaways:
- Use the arguments object, when you can call a function with more number of arguments than it is formally declared to accept.
- arguments object is also useful when you don't know in advance how many arguments will be passed to the function
- You can use arguments.length to determine the number of arguments actually passed to the function
- Once you know the length, you can access each argument using the arguments object.
The Return Statement
The return statement ends function execution, and specifies a value to be returned to the calling function. It is the last line in any function.
Any code written after the return statement is "unreachable" and throws a warning: Warning: unreachable code after return statement
Syntax:
Here expression is the value that will be returned, and if it is omitted then "undefined" is returned. For example, the following function returns the "cube" of it's argument a, where a is any number.
Output:
If we do not pass any value to the cube function, then undefined will be returned.
Returning a function
JavaScript Functions are so amazing that you can actually return them. Let's see how -
Output:
So, in the above example we see, the function calc() (which multiplies any value by 2), is being returned by the function myFunc(). We call myFunc() and store it's result in the variable a (here a will now store the calc(x){...} function returned by myFunc()). Again, we call a() passing 2 as a parameter. Now, the value will be evaluated by the calc() function and returned. Finally we log the result into the console which is 4. Not only this, we can also call the calc() directly by:
One way to call the returned function from the myFunc function without variable assignment is by using parentheses () two times ()() like this myFunc()(2). Let's break myfunc()(2) in parts and see how it works :
- Here myfunc() will return "calc". So: myFunc()(2) = calc(2)
- Then 2 is passed into this calc(x) function; which is now calc(2).
- Finally the value will be evaluated and returned by calc(2) and stored in the variable answer.
Some important points to note are:
- A function immediately stops at the point where return is called.
- No line terminator is allowed between the return keyword and any expression(suppose a semicolon).Otherwise, the rest of the code will be considered as unreachable.
- A function can return another function. The function which is returned by any function, preserves(or remember) the value of the elements present in it's parent function, and forms a closure. For more details, refer to closure.
- The return statement is not mandatory. We may have javascript functions do not return any value at all. In that case, when the function execution ends, it automatically returns the control to the calling statement.
Advantages of JavaScript Functions
There are several usages of JavaScript Functions. You simply can’t do anything in JavaScript without them. Some of them are stated below:
- Code Reusability hence lesser lines of code
- Without functions, writing asynchronous code in JavaScript is almost impossible because JavaScript is an asynchronous language
- ES6 or modern JavaScript is all about functions
- A simple example(yet important usage) of the JavaScript function is setting a timer: this requires a callback function. Similarly, all event listeners use callback functions to respond to events like mouse clicks or scrolling. This cannot be done without JavaScript functions.
Note: A callback is a function passed as an argument to another function.
Example:
In this example,
- setTimeout() is a method of the window object.
- setTimeout() sets a timer(3000 ms) and executes a callback function(demo()) when the timer expires.
- Hence, the above code logs "setTimeout Demo" in the console after 3 seconds.
Functions are everywhere and near to everything in JavaScript!
Function Hoisting
In JavaScript, hoisting is the process in which the declarations of functions, variables, and classes are moved to the top of the scope before execution of the code. Hoisting allows functions to be safely used in code before they are declared.
Example:
Output:
This is due to hoisting that we can use the function before declaring it in our code. Otherwise we would have to write the same code like:
The same does not follows for the arrow functions. Like traditional function expressions, arrow functions are not hoisted, so you cannot call them before you declare them. If we try to access them before declaration, we will get 'undefined'. Let us see the below example:
Output:
This is because, the arrow functions are treated like variables in JavaScript. They do not have any names(anonymous). So, undefined is allocated to them in the memory allocation phase. Variable and class declarations are also hoisted. Although, they are not in the scope of this tutorial.
Function Scope
What is scope? Scope refers to the visibility of variables, functions, and objects within some part of your code during runtime. In simple words, scope determines the visibility of variables and other resources in particular areas of your code.
A function's scope can be either its body, which declares local variables, or the scope that contains the function's name (either a class or namespace). Let us see some important points about a function's scope:
-
Local Scope: When a variable is defined inside a function, it is said to be in local scope. A javascript function can only access a variable that is present inside its scope or the global scope. It cannot access a variable that is defined in some other function.
Example 1:
Output:
As expected, we can easily access the value of a here. Let's see if we try to access it outside of the function --
Example 2:
Output:
So, we cannot access a from anywhere other than inside the function demo() itself. Because a have the local scope and is only accessible inside the function demo() where it is declared.
Key Takeaway:
- A variable that is defined in the local scope can only be accessed by the function where it is declared, or the children of the function. It cannot be accessed by any other function outside the one where it is declared.
Local variables are automatically deleted when the function is completed.
-
Global Scope : Variables defined outside of a function, are said to be in a global scope. In a script, the outermost scope is the global scope.
Example:
Output:
In this example, we can access the variable val from any function, since it is defined in the global scope.
Key Takeaway:
- A variable which is declared in the global scope can be accessed by all the functions.
In a web browser, global variables are deleted when we close the browser window (or tab).
-
Lexical Scope: This means that in a nested group of functions, the inner functions will have access to the variables and the other resources of their parent scope.
Example:
Output:
Here, anotherDemo() is defined inside demo() so, we may say anotherDemo() is the inner function and demo() is it's parent. And the inner function will have access to all the variables or methods defined inside it, or its parent. So, we can access the value of a & b both.
However, if you try to access 'b' from demo(), you will not be able to access it because it is not inside the scope of it's child.
Example:
Output:
Key Takeaway:
- An inner function can access the variables and other resources of it's parent function
- Vice versa is not applicable.
Here the memory can be freed up only when the innermost function is no longer accessible.
Scope and the Function Stack
Recursion
Recursion simply means when a function can 'refer' or 'call' itself to solve a problem. And a function that calls itself is a recursive function.
There are 3 ways in which a function can refer to itself:
- The function's name
- arguments.callee - The arguments.callee property contains the currently executing function.
- An in-scope variable that refers to the function
Note: callee is a property of the argument's object. It can be used to refer to the currently executing function inside the function body of that function. It is useful when the name of the function is unknown, for example, an "anonymous function".
Warning: The edition of ECMAScript (ES5) forbids use of arguments.callee() in strict mode.
Example:
Output:
In the above example,
- factorial is the function's name
- fact is the variable which is referring to the function.
Recursion uses a stack: the function stack. It is also known as the call stack. Whenever a function call takes place, it is pushed into the stack and the execution happens. Likewise, different function calls result in the pushing of the functions in the stack. Once the execution is completed, that particular function is popped off from the stack. However, if the stack limit exceeds what it is assigned to, it results in a "stack overflow" error. Read more of call stack in the MDN Web Documentation.
Nested functions and closures
A function within another function is known as the nested function. In the nested functions, the inner functions have access to the variables or other resources of its parent(or the outer function). Let's take an example to understand this --
Output:
So, here, we see that the inner function has access to it's outer and outermost function's variables (and all other resources). But vice versa is not applicable where outer() will be able to fetch the local variables of inner(). Simply put, the parent function won't be able to access the variables defined in their subsequent child functions.
Closures
Closure means that an inner function always has access to the variables and parameters of its outer function. The inner function preserves the value of the outer function, even after the outer function has returned.
In other words, a closure is a function having access to it's parent scope, even after the parent function's execution is completed.
It's concept is deeply related to the lexical scope, we just studied.
Scope Chain: In JavaScript, when a variable is used, the JavaScript engine will seek the variable's value in its current scope. If it is unable to find the variable, it will seek the outer scope and continue this until it finds the variable or reaches global scope. If it cannot find it till the end then either it will declare the variable implicitly or will throw an error.
How does a closure looks?
A closure looks like this:
- An outer function
- An inner function
- A return statement that references the inner function
You can use a closure to put one function inside the other, and still execute the inner function at a later point of time, preserving the variables of its parent function.
Output:
This is the simplest example of closure where the inner function have access to the variables of it's parent. At some later point of time, we may call the inner function, and get access to the variables of its parent function as well.
When a closure executes?
Since closures exist within outer functions, they allow you to execute the outer function at one point in time, and then execute the closure later, with the values from the outer function saved.
Closures have access not only to the variables defined in their outer function but also to the arguments of the outer function.
Preservation of variables
A closure preserves the arguments and variables in all scopes it references(or points to). Closures allow you to save a snapshot of the scope when the function was originally declared. Since they exist within outer functions, they allow you to execute the outer function at any point of time, and then execute the closure later, with the values from the outer function saved.
Let us see a simple example which shows how closures preserve any variable from their parent's scope.
Example:
Output:
Notice how x is preserved when 'child' is returned. Since each call provides different arguments, a new closure is created for each call to 'parent'. The memory can be freed only when the returned 'child' is no longer accessible.
Multiply-nested functions:
Functions can be multiply-nested. For example:
- A function (GrandFather) contains a function (Father), which itself contains a function (Son).
- Both functions Father and Son form closures here. So, Father can access GrandFather, and Son can access Father.
- In addition, since Son can access Father, which can access GrandFather, Son can also access GrandFather.
Example:
Output:
So this example is self-explanatory where the functions and their scope of access is detailed.
Name conflicts
When two arguments or variables in the scopes of a closure have the same name, there is a name conflict. The inner-most function takes the highest precedence, while the outer-most function takes the lowest in terms of variable name resolution. Let us understand this better with an example:
Output:
The name conflict happens at the statement return x * 3 and is between anotherDemo's parameter x and demo's variable x. The scope chain here is {anotherDemo -> demo -> global object}. Therefore, anotherDemo's x takes precedence over demo's x, and 30 (anotherDemo's x) is returned instead of 50 (demo's x).
Pre-defined Functions
Pre-defined functions are in-built javascript functions. They are already defined and can be directly used by us.
Unlike custom functions, pre-defined functions are built into the software and do not need to be created by a programmer. Pre-defined functions carry out common tasks, such as determining the length of a string or parsing some data types, etc. JavaScript has several top-level pre-defined(or built-in) functions:
- eval(): The eval() method evaluates JavaScript code represented as a string. NEVER use eval()! Read why.
- isFinite(): The global isFinite() function determines whether the passed value is a finite number. It also converts the parameter to a number, if required.
- isNaN(): The isNaN() function determines whether a value is NaN or not.
There are several other pre-defined functions like parseInt(), decodeURI(), decodeURIComponent(), encodeURI(), encodeURIComponent(), etc. You can read more about them in the MDN Web Documentations.
Conclusion:
- When you create a function with a name, its parameter list, and function statements, it is called a function declaration.
- In function expressions, we just omit the name of the function(making it an anonymous function), and the remaining syntax is similar to normal functions. We assign the function expressions to variables.
- JavaScript functions can be invoked with any number of arguments, regardless of how many arguments are specified(or mentioned) in the function definition.
- Function parameters have a default set to undefined. So, we should set a different default value for the parameters to prevent any unexpected results.
- The rest parameter syntax allows a function to accept a variable number of arguments as an array.
- Javascript provides an in-built object called arguments. Using it, we can call a function with more arguments than it is formally declared to accept.
- The return statement is the last line of any function, which returns a value to the calling function, and ends function execution. However, it is optional.
- Advantages of JavaScript function: Promotes code re-usability, makes JavaScript work asynchronously, and functions are first-class citizens; hence they can be used in a lot more ways in JavaScript.
- Scope defines where your variables will be accessible throughout your script.
- Hoisting is a JavaScript process where variables and function declarations are moved to the top of their scope before code execution.
- A function within another function is known as the nested function. Here, the inner functions have access to the variables or other resources of its parent(or the outer function)
- Closure basically means that an inner function always has access to the variables and parameters of its outer function. The inner function preserves the value of the outer function, even after the outer function has returned.
- Pre-defined functions are in-built javascript functions. They are already defined and can be directly used. Example: parseInt(), isNan(), etc.