A Function can be understood as a block of code written by a user which is defined once in a program but can be executed or invoked any number of times anywhere in the program.
- The concept of functions has another name which we are familiar with, i.e. subroutine and procedure.
- When functions are invoked, they are provided with values called arguments to the function’s parameters.
- In each function invocation, there is a distinct value called the invocation context which is the value of the this keyword.
- The function keyword can be used as a declaration or as an expression.
- ES6 introduces a new way to define functions without using the function keyword. Instead, the arrow functions are used.
- The arrow functions have a compact syntax.
- Arrow functions are useful in passing one function as an argument to another function.
- Another way of defining Functions is by using Function() Constructor.
- Identifier naming the function, as the name of the function should be there while declaring a function. It is used as the name of a variable, then the defined function object is assigned to the variable.
- It must have a pair of parathesis closed around a list of zero or more identifiers separated by comma. Such identifiers are the parameter names of the function and behave like local variables inside the body of the function.
- It must have a pair of curly braces containing zero or more code statements inside it. These statements are the body of the function and executed when the function is invoked.
- Here, a function printprops(o) is declared with o as parameter.
- The body of the function has a console.log() statement that prints the name and value of each property of o.
- Here, a function distance(x1, x2, y1, y2) is declared with x1, x2, x3, and x4 as parameters.
- The body of the function Math.sqrt() function that computes and returns the distance between Cartesian points (x1, y1) and (x2, y2).
- Here, a recursive function factorial(x) is declared with x as parameter.
- The body of the function has a recursive statement that computes the factorial of the value of x.
- Here, in line 6, the function expression defines a function that squares its argument. It is assigned to a variable square.
- In line 8, the function expressions include names, which is useful for recursion.
- In line 10, the function expressions are used as arguments to other functions.
- In line 12, the function expressions are defined and immediately invoked.
We must keep in mind that there is an important difference between defining as function f() with a function declaration and assigning a variable with a function after creating the function as an expression.
- The syntax of the Arrow Function is written using mathematical notation => which also separates the function parameters from the function body.
- The function keyword is not used for Arrow Functions, since they are expressions instead of statements.
- The general form of an Arrow Function is a comma-separated parameter list in parentheses followed by => which is then followed by the function body in the curly braces.
Another syntax of Arrow Function that is used if the body of the function is a single return statement, we can omit the return keyword. The semicolon and the curly braces go with it. Only the body of the function is written as an expression whose value is to be returned.
Also, we can omit the parentheses around the parameter list when the arrow function has exactly one parameter.
We should keep it in our mind that an arrow function having no arguments must be written with empty parentheses.
In situations where the body of an arrow function is a single return statement but the expression that is to be returned is an object literal, then we must put that object inside parentheses to avoid any syntactic ambiguity between curly braces of a function body and the curly braces of an object literal.
- Here, in line 6, f() returns an object.
- In line 8, g() returns an object.
- In line 10, h() returns nothing (ambiguous syntax).
- In line 12, there is a syntax error due to ambiguity.
In the above example, we can clearly note that the function square() is nested within the function hypotenuse().
Nested Function obeys the variable scoping rule that the parameters and variables of the function can be accessed by the function nested within.
The functions can be invoked in different ways:
- Invoking as Functions
- Invoking as Methods
- Invoking as Constructors
- Invoking indirectly using call() and apply()
- Invoking implicit Function
We will discuss each of these ways in detail with suitable examples.
Invoking as Functions
- Here, in an invocation, each of the arguments is evaluated and the computed value then becomes the value of the invocation expression.
- These values are assigned to the parameters in the function definition.
- Inside the function body, a reference to a parameter evaluates to the corresponding argument value.
Invoking as Methods
If a function f and an object are in a program, then we can define a method m of o by:
o.m = f;
Then we can invoke the method m() of the object o by:
If we have two arguments for m(), it can be invoked by:
We will take another example to understand the method invocation of functions.
Invoking as Constructors
In a constructor invocation, expressions in the argument list in parenthesis are evaluated and passed to the function as passed in a function or method invocation.
o = new Object(); o = new Object;
- In the constructor invocation of a function, a new empty object is created that inherits from the object specified by the prototype property of the constructor.
- This newly created object can be used as the invocation context, so the constructor function can refer to it with the keyword this.
Invoking indirectly using call() and apply()
- The call() and apply() methods allow us to explicitly specify the this value for the invocation that means any function can be invoked as a method of any object, even if it’s not actually a method of that object.
- The call() method has its own argument list as arguments to the function.
- The apply() method expects an array of values that is to be used as arguments.
Invoking implicit Function
- When using such features, we have to be very careful when writing functions that are to be implicitly invoked.
- Any wrong use of such features can throw bugs, cause side effects, and performance issues in these functions that are harder to diagnose and fix than in any regular functions.
Further, we will discuss the behavior of a function under certain conditions like when a function is invoked with fewer arguments than the number of declared parameters or with more arguments than the number of declared parameters.
Optional parameters and Defaults
In situations when a function is invoked with fewer arguments than declared parameters, the additional parameters are set to their default value, which is generally undefined.
- Here, we can see that the function getPropertyName() can be invoked with one or two arguments.
- In line 6, two objects are created for testing.
- In line 7, a == [“x”] will get o’s properties in a new array.
- In line 9, a == [“x”, “y”, “z”] will add p’s properties to it.
Rest Parameters and Variable-Length Argument Lists
In a situation when we have to write functions that can be invoked with fewer arguments than the parameter, then we can use parameter defaults.
Rest parameters do the just opposite, they enable us to write functions that can be invoked with arbitrarily more arguments than parameters.
- Here, the first argument is assumed as the biggest.
- Then the rest of the arguments are looped for finding the bigger argument.
- Then the biggest argument value is returned.
The identifier arguments refer to the Argument object for the invocation of varargs functions.
The Arguments object is an array-like object that retrieves the argument values passed to the function by a number rather than by name.
However, we should avoid using Argument Object as it is inefficient and hard to optimize.
Spread Operator for Functions