Functions

Functions are JavaScript codes that you declare once and can be called (invoked) multiple times. A function that is attached to an object or a class is also called the method of that object or class.

  • When you declare a function, you can specify their parameters, which behave like local variables of the function whose value will be decided when you call the function.
  • When you call a function, you provide the values (arguments) for the parameters.
  • You may use the return keyword inside a function to return a value (exit the function with the given value).

Creating a function

You can create a "normal" function (using the function keyword) or an "arrow" function (using the => notation).

Using the function keyword

You may provide the name of the function after the function keyword, before the open paranthesis. This name of the function becomes a local variable inside the function itself, which you can call again, creating a recursive function ("a function that calls itself").

You can provide the parameters between the ( and ), separated by ,.

function factorial(n) {
  if (n === 0 || n === 1) {
    return 1;
  } 
  return n * factorial(n - 1);
}

console.log(factorial(5)); //=> 120 = 5!

Using the => notation

On the left-hand side of =>, you put:

  • the list of parameters if there is only one parameter, you can omit the ().

On the right-hand side of =>, you put:

  • a block of statements to run (using {}), OR
  • a value to return (e.g., x => x + 1 is a shorthand of (x) => { return x + 1 }). Due to ambiguous syntax, if you want to immediately return an object, make sure to wrap it with (), e.g. x => ({ value: x })
const arr = [1, 2, 3];
arr.forEach((el, index) => {
  console.log(`arr[${index}] = ${el}`);
});

Using new Function

You can also create a function from a string of JavaScript code using new Function(...parameterNames, functionBody). The scope for functionBody would be the global scope.

const f = new Function('x', 'y', 'return x + y');
console.log(f(2, 5)); //=> 7

Function parameters

Parameter's default value

By using = after a parameter's name, you can assign the default value for that parameter.

function logMessage(greetings = 'Hello', name = 'user') {
  console.log(greetings + ', ' + name + '!');
}

logMessage('Hi'); //=> Hi, user!

Rest parameters

By using the spread operator (...), you can make your function receive any number of arguments.

function testFn(a, b, ...rest) {
  console.log(a); //=> 1
  console.log(b); //=> 2
  console.log(rest); //=> [3, 4, 5]
}

testFn(1, 2, 3, 4, 5);

Invoking functions

As a function

Use the () operator to call the function, specifying inside it the list of arguments.

function total(a, b, ...rest) {
  console.log(a); //=> 1
  console.log(b); //=> 2
  console.log(rest); //=> [3, 4, 5]
}

testFn(1, 2, 3, 4, 5);

You can also use the spread operator (...) to "spread" an iterable (e.g. an array or a string) into "comma-separated values" for the arguments.

const arr = [];
const numbers = [1, 2, 3];

// This is equivalent to:
// arr.push(1, 2, 3);
arr.push(...numbers);

As a constructor

You can call a function as a constructor using the new keyword, specifying inside it the list of arguments. (You can also use the spread operator as mentioned above.)

function Person(firstName, lastName) {
  this.firstName = firstName;
  this.lastName = lastName;
}

const person = new Person('James', 'Smith');
console.log(person);

A constructor function typically does not use the return keyword, and it returns the object being created. If you use a return, though:

  • if you return a primitive value, it is ignored (it still returns the object being created)
  • if you return an object, then it becomes the resolved value of the invocation expression

Indirectly using .apply() or .call()

You can use the following to indirectly call a function:

  • functionName.apply(thisArg, argsArray), or
  • functionName.call(thisArg, ...args)

Using these allows you to override the this value received by your function.

function greetUser(menuA, menuB) {
  console.log('Hello, ' + this.firstName + ' ' + this.lastName + '.');
  console.log('Do you want ' + menuA + ' or ' + menuB + '?');
}

const user = { firstName: 'Joe', lastName: 'Lamy' };
// `user` will become `this` inside `greetUser`
greetUser.apply(user, ['coffee', 'tea']); // "A is for array"
greetUser.call(user, 'coffee', 'tea'); // "C is for comma"

To help you remember

  • Apply expects an array ("A is for array")
  • Call expects comma-separated values ("C is for comma")

Special variables

this

Non-arrow functions have this variable defined inside it. The value depends on how you call a function. You can override it by using bind, apply, or call when calling a function.

  • If you call a function as a function this will be the global object (globalThis) while not in strict mode, and undefined while in strict mode.
  • If you call a function as a method of an object this will be the object on which you call the function.

Arrow functions do not have their own this variable, so they inherits this variable from their enclosing scope. (Sometimes a useful feature of arrow functions.)

arguments

Non-arrow functions have arguments variable defined inside it. It behaves like an array, but it only has the length property (apart from its elements).

This was the only way to create functions that accept any number of arguments ("varargs functions") before rest parameters was introduced in ES6. You should just use rest parameters in modern JavaScript.

function sum() {
  let total = 0;
  for (let i = 0; i < arguments.length; i++) {
    total += arguments[i];
  }
  return total;
}

console.log(sum(4, 5, 6)); //=> 15
console.log(sum(1, 4, 7, 9)); //=> 21

The function instance

When you have a function instance (let's call it fn), it has the following properties/methods.

Properties

fn.name

The name of the function.

fn.length

The number of parameters of the function, NOT including rest parameters.

function func1() {}
function func2(a, b, c, d, ...z) {}
console.log(func1.length); //=> 0
console.log(func2.length); //=> 4

Methods

fn.apply(thisArg, argsArray)

Calls function fn with the given this value and array of arguments.

fn.call(thisArg, ...args)

Calls function fn with the given this value and comma-separated arguments.

fn.bind(thisArg, ...args)

Creates a new function that when called has this value of thisArg (required), and its first parameters are ...args (optional).

(The latter behavior of bind is akin to partial application or currying in functional programming.)

let add = (x, y) => x + y;
let boundAdd = add.bind(null, 1);
console.log(boundAdd(2)); //=> 3 (1 + 2 = 3)

References