All writing

Functions in JavaScript

JavaScript

A function is a block of JavaScript code that is defined once but may be executed, or invoked, any number of times. JavaScript functions are parameterized (function definition may include a list of identifiers called parameters, that work as local variables for the body of function).

If a function is assigned to a property of an object, it is called method of that object. When a function is invoked through an object, that object is this value for the function.

In JavaScript, functions are objects.

Defining Functions

A function definition consists of the function keyword, followed by:

  • The name of the function
  • A list of parameters to the function, enclosed in parentheses and separated by commas
  • The JavaScript statements that define the function, enclosed in curly braces, { /* … */ }
function sum(num1 + num2) {
return num1 + num2
}

The function sum takes two parameter, num1 and num2. The function consists of one statement that returns the parameter of the functions added to each other. The return statement specifies the value returned by the function, which is num1 + num2.

Parameters are passed to function by value. If the code within the function assigns new value to the parameter, the change is not reflected globally or in the code which called the function.

However, when you pass an object as a parameter and if the function changes the property of object then the change is reflected outside the function.

function myFunc(obj) {
obj.name = "Doe";
}
const user1 = {
name: "Joe",
address: "Nepal",
dob: "1998",
};
console.log(user1.name); // "Joe"
myFunc(user1)
console.log(user1.name); // "Doe"

Similarly, if you pass an array as a parameter and if the function changes the values of the array then the change is reflected outside the function.

function myFunc(arr) {
arr[0] = 20;
}
const arr = [30];
console.log(arr[0]); // 30
myFunc(arr);
console.log(arr[0]); // 20

Function expressions

Function expressions are similar to function declarations, but they appear within the context of a larger expression or statement, and the name is optional.

const square = function(x) {return x*x;};
// Note that we assign it to a variable
// Function expressions can also be used as arguments to other functions
[3,2,1].sort(function(a,b) {return a-b; });

Arrow Functions

In ES6, function can be defined using compact syntax called arrow functions. This syntax uses an => “arrow” to separate the function parameters from the function body. The function keyword is not used, and function name is also not needed.

const sum = (x, y) => {return x + y;};

Arrow functions support an even more compact syntax. If the body of the function is a single return statement, the return keyword can be omitted.

const sum = (x, y) => x + y;

Furthermore, if an arrow function has exactly one parameter, the parenthesis around the parameter can also be omitted.

const square = x => x*x;
Note: An arrow function with no arguments must be written with an empty pair of parentheses.

Nested Functions

In JavaScript, functions may be nested within other functions.

function greetUser(firstName, lastName) {
function getFullName(){
return firstName + ' ' + lastName;
}
alert("Hello, " + getFullName());
}

The variable scoping rule for nested function is different; they can access the parameters and variables of the function (or functions) they are nested within. In above example, the function getFullName() can read and write the parameters firstName and lastName defined by the outer function greetUser().

Invoking Functions

The body of a function is not executed when the function is defined, but when rather when it is invoked. JavaScript functions can be invoked in following ways:

  • As functions
  • As methods
  • As constructors
  • Indirectly through their call() and apply() methods
  • Implicitly, through JavaScript features that do not appear like normal function invocations

Function Invocation

An invocation expression consists of function expression that evaluates to a function object followed by an open parenthesis, a comma-separated list of zero or more argument expressions, and a close parenthesis.

printprops({x:1});
let probability = factorial(5)/factorial(13);

For regular function invocation, the return value of the function becomes the value of the invocation expression. If the function returns because the interpreter reaches the end, the return value is undefined. If the function returns because the interpreter executes a return statement, then the return value is the value of the expression that follows the return or is undefined if the return statement has no value.

function sum(num1 + num2) {
return num1 + num2
}
sum(10, 20) // return 30

Invocation context (the this value) in non-strict mode is global object. In strict mode, the invocation context is undefined. The functions defined using the arrow syntax behave differently: they always inherit the this value that is in effect where they are defined.

Method Invocation

A method is a JavaScript function that is stored in a property of an object. If there is a function f and an object o, a method named m of o can be defined by:

o.m = f;

Having defined the method m() of the object o, invoke it as:

o.m();

Method invocations differ from function invocations in one important way, the invocation context. Property access expressions consist of two parts: an object (in this case o) and a property name (m). In method-invocation expressions, the object o becomes the invocation context, and the function body can refer to that object by using the keyword this.

Most method invocations use the dot notation for property access, but property access expressions can be as following as well:

o["m"](x, y) // Another way to write o.(x,y)
a[0](z) // Also a method invocation(assuming a[0] is a function)
customer.surname.toUpperCase(); // Invoke method on customer.surname
f().m(); // Invoke method m() on return value of f()
Note:
Method chaining
When methods return objects, the return value of one method invocation can be a part of subsequent invocation. This results in a series (or chain) of method invocations as a single expression.
Note:
this is a keyword, not a variable or property name. JavaScript syntax does not allow to assign a value to this.

Constructor Invocation

If a function or method invocation is preceded by keyword new, then it is constructor invocation. Constructor invocations differ from regular function and method invocations in handling of arguments, invocation context and return values.

A constructor invocation creates empty object that inherits from the object specified by the prototype property of the constructor. Constructor functions are intended to initialize objects. This newly created object is used as invocation context, so the constructor function can refer to it with this keyword.

Indirect Invocation

JavaScript functions are objects which have methods, methods like call() and apply() which invoke the function indirectly. Both of these methods explicitly specify the this value for the invocation, which means any function can be invoked as a method of any object, even if it is not actually a method if that object.

Implicit function invocation

The language features that can cause implicit function invocation include:

  • If an object has getters or setter defined, then querying or setting the value of its properties may invoke those methods.
  • When an object is used in a string context, its toString() method is called. Similarly, when an object is used in a numeric context, its valueOf() method is called.
  • When the elements of an iterable objects are looped, there are number of method calls that occur.
  • A tagged template literal is a function invocation in disguise.
  • Proxy objects have their behavior completely controlled by function.

Function Arguments and Parameters

function sum (num1, num2) {
return num1 + num2;
}
sum(20,10);

A parameter is one of the variables in a function. And when a method is called, the arguments are the data passed into the method’s parameters. When the function sum is called with sum(20,10) the arguments 20 and 10 are assigned to num1 and num2.

Optional Parameters and Defaults

When a function is invoked with fewer arguments than declared parameters, the additional parameters are set to their default values, which is normally undefined.

In ES6 and later, a default value can be defined for each of function parameters directly in the parameter list of function.

function getName(o, a=[]) {
for(let name in o) a.push(name);
return a;
}

Parameter default expressions are evaluated when function is called, not when it is defined, so each time getName() is invoked with one argument, a new empty array a = [] is created and passed.

Rest Parameters and Variable-Length Argument Lists

Parameter defaults enable to write functions that can be invoked with fewer arguments than parameters. Rest parameters enable the opposite by allowing to write function that can be invoked with arbitrarily more arguments than parameters.

function max(first=-Infinity, …rest) {
let maxValue = first;
for(let n of rest) {
if (n > maxValue) {
maxValue = n;
}
}
return maxValue;
}
max(1, 10, 100, 2, 3, 1000, 4, 5, 6);

A rest parameter is preceded by three periods and should be last parameter in a function declaration. When a function with a rest parameter is invoked, the arguments passed are first assigned to the non-rest parameters, and then remaining arguments are stored in an array that becomes the value of the rest parameter. The array may be empty, but a rest parameter will never be undefined.

The Spread Operator for Function Calls

The spread operator … is used to unpack, or spread out the elements in an array (or any other iterable object) in a context where individual values are expected.

let numbers = [5, 2, 10, -1, 9, 100, 1];
Math.min(…numbers); // => -1
Note:
… is not a true operator in the sense that it cannot be evaluated to produce a value.

When we use … syntax in function definition than a function invocation. It has opposite effect to spread out operator. … in a function definition gathers multiple function arguments into an array.

Destructuring Function Arguments into Parameters

When a function is defined with parameter names within square brackets, the function expects an array value to be passed for each pair of square brackets. As part of the invocation process, the array arguments will be unpacked into the individually named parameters.

function vectorAdd([x1,x2],[x2,x3]){ // unpack 2 arguments into 4 parameters
return [x1 + x2, y1 + y2];
}
vectorAdd([1,2],[3,4]); // => [4,6]

Function as Values

In JavaScript, functions are not only syntax but also values, i.e. they can be assigned to variables, stored in properties of objects or elements of arrays, etc.

function square(x) { return x*x; }
let s = square // Now s refers to the same function that square does
square(4) // => 16
s(4) // => 16
let o = {square: function(x) {return x*x; }}; // An object literal
let y = o.square(16); // y => 256

Functions as Namespaces

As variables declared within a function are not accessible outside of the function, it is sometimes useful to act as temporary namespace in which variables can be defined without cluttering the global namespace.

function chunkNamespace() {
// Chunk of code goes here
// Any variables defined in the chunk are local to this feature
// instead of cluttering up the global namespace
}
chunkNamespace();

This code defines only a single global variable chunkNamespace. If defining even a single property can clutter the global namespace then an anonymous function can be called.

(function() { //chunkNamespace() function rewritten as an unnamed expression
// chunk of code goes here
}()); // End the function literal and invoke it now

This use of functions as namespaces becomes really useful when one or more functions inside the namespace function using variables within that namespace, but then pass them back out as the return value of the namespace function. Functions like this are called closures.

Closures

Note:
Lexical scoping means that function are executed using the variable scope that was in effect when they are defined, not the variable scope that is in effect when they are invoked.

In order to implement lexical scoping, the internal state of a JavaScript function object must include not only the code of the function but also a reference to the scope in which the function definition appears. This combination of a function object and a scope in which the function’s variables are resolved is called a closure. This allows the nesting of functions and grants the inner function full access to all the variables and functions defined inside the outer funciton. However, the outer function does not have access to the variables and function defined inside the inner funciton which provides a sort of encapsulation.

// The outer function defines a variable called "name"
const pet = function (name) {
const getName = function () {
// The inner function has access to the 'name' variable of the outer function
return name;
}
return getName(); // Return the inner function, therby exposing it to outer scopes
};
const myPet = pet("Vivie");
console.log(myPet()); // 'Vivie'

Function Properties, Methods, and Constructor

The length Property

The length property of a function specifies the number of parameters it declares in its parameter list.

The name Property

The name property of a function specifies the name that was used when the function was defined. This property is useful while debugging or writing error messages.

The prototype Property

All functions, except arrow function have a prototype property. When a function is used as constructor, the newly created object inherits properties from the prototype object.

The call() and apply() methods

call() and apply() allow to indirectly invoke a function as if it were a method of some other object.

call() is a function that heps change the context of the invoking function. It helps replace the value if this inside a function with other value.

function Car(type, fuelType) {
this.type = type;
this.fuelType = fuelType;
}
function setBrand(brand) {
Car.call(this, "convertible", "petrol");
this.brand = brand;
console.log("Car details= ", this);
}
function definePrice(price){
Car.call(this, "convertible", "diesel");
this.price = price;
console.log("Car details= ", this);
}
const newBrand = new setBrand('Brand1');
const newCarPrice = new definePrice(10000);

apply() is similar to call(). The only difference is that in apply() an array can be passed as an argument list.

function Car(type, fuelType){
this.type = type;
this.fuelType = fuelType;
}
function setBrand(brand){
Car.apply(this, ["convertible", "petrol"]); //Syntax with array literal
this.brand = brand;
console.log(`Car details = `, this);
}
function definePrice(price){
Car.apply(this, new Array("convertible", "diesel")); //Syntax with array object construction
this.price = price;
console.log(`Car details = `, this);
}
const newBrand = new setBrand('Brand1');
const newCarPrice = new definePrice(100000);

The bind() method

The primary purpose of bind() is to bind a function to an object. When bind() method is invoked on a function f and an object o is passed, the method returns a new function. Invoking the new function f invokes the original function f as a method of o.

function f(y) { return this.x+y;} // This function needs to be bound
let o = { x:1}; // An object we'll bind to
let g = f.bind(o); // Calling g(x) invokes f() on o
g(2); // => 3
let p = { x: 10, g}; // Invoke g() as a method of this object
p.g(2); // => 3: g is still bound to o, not p

The toString() Method

The toString() method return as a string, string object, numbers as a string.

let text = "Hello World!";
let result = text.toString();

The Function() constructor

As functions are objects, there is a Function() constructor that can be used to create new functions.

const f = new Function("x", "y", "return x*y;");

Key points to understand about Function() constructor:

  • The Function() constructor allows JavaScript functions to be dynamically created and compiled at runtime.
  • The Function() constructor parses the function body and creates a new function object each time it is called.
  • The function is creates do not use lexical scoping, instead they are always compiled as if they were top-level functions.

REFERENCES