You may have heard that JavaScript supports first-class functions. This means it treats them like any other value. Functions can be assigned to variables, stored in data structures, returned from other functions, and passed to other functions as arguments.

Functions in JavaScript

Despite the common misconception, not everything is an object in JavaScript. However, functions are just callable objects. In technical terms, they are Function objects with an internal [[Call]] method.

Function declarations

If a declaration begins with the function keyword, it's a function declaration.

function greet(name) {
return `Hello, ${name}!`;
}

Function declarations are hoisted, meaning they can be called before they're declared.

greet("World"); // "Hello, World!"

function greet(name) {
return `Hello, ${name}!`;
}

Function expressions

If the function keyword appears elsewhere, it's probably a function expression.

// Assigned to a constant
const fn = function () {};

// Element in an array
const functionList = [function () {}];

// Property of an object
const obj = {
fn: function () {},
};

// Returned from another function
function fn() {
return function () {};
}

// Argument to another function
fn(function () {});

// Immediately-invoked function expression (IIFE)
(function () {
console.log("Hello, World!");
})();

Function expressions are not hoisted. They may be named or anonymous. If named, the name is local to the function body.

// Named, but `bar` can only be referenced in the function body
const foo = function bar() {};
console.log(foo.name); // "bar"

// Anonymous, but the name is inferred by the `const` declaration
const baz = function () {};
console.log(baz.name); // "baz"

// Truly anonymous
console.log(function () {}.name); // ""

Arrow functions

Since ECMAScript 2015 (ES2015/ES6), JavaScript has also supported arrow functions, which are a special type of function expression. They are similar to lambda expressions in other languages. Although frequently used for their brevity, they differ from "normal" function expressions in a few important ways. For example, they don't have their own binding to the this keyword.

// "Normal" function expression
const sum = function (a, b) {
return a + b;
};

// Arrow function expression with implicit `return` statement
const sum2 = (a, b) => a + b;

Assigning a function to a variable

A function can be assigned to a variable or constant. As mentioned above, function expressions are not hoisted, meaning they can't be called before they're defined.

// Assigned to a variable
let greet = function (name) {
return `Hello, ${name}!`;
};

// Assigned to a constant
const sum = (a, b) => a + b;

Storing a function in a data structure

A function can be stored in a data structure such as an array or object literal. It can even be stored in a Map, Set, WeakMap, or WeakSet.

Element in an array

A function can be an element in an array. For example, here's an array of math functions. An array is probably not the ideal data structure for this—it would be easier to refer to the functions by name—but it is perfectly valid.

const mathFunctions = [
function (a, b) {
return a + b;
},

function (a, b) {
return a - b;
},

function (a, b) {
return a * b;
},

function (a, b) {
return a / b;
},
];

mathFunctions[0](4, 4); // 8
mathFunctions[1](12, 4); // 8
mathFunctions[2](2, 4); // 8
mathFunctions[3](24, 3); // 8

The useState() hook in React provides a common example of a function as an array element. It returns an array with two elements: the state at index 0 and a function to update it at index 1. React developers normally use array destructuring to make the hook more readable, but here's how it might look like without it. Note that setCount is assigned to the value of state[1], which is a function.

function Counter(props) {
const state = useState(props.count ?? 0);

const count = state[0];
const setCount = state[1];

function increment() {
setCount(count + 1);
}

return (
<div>
<button type="button" onClick={increment}>
Increment
</button>

<p>The count is {count}.</p>
</div>
);
}

Property of an object

A function can also be a property of an object, which is much more common, although technically the indices of an array are still just object properties.

const mathFunctions = {
add: function (a, b) {
return a + b;
},

subtract: function (a, b) {
return a - b;
},

multiply: function (a, b) {
return a * b;
},

divide: function (a, b) {
return a / b;
},
};

mathFunctions.add(4, 4); // 8
mathFunctions.subtract(12, 4); // 8
mathFunctions.multiply(2, 4); // 8
mathFunctions.divide(24, 3); // 8

Since ECMAScript 2015, you will more commonly see function properties written as method definitions, although method definitions are not 100% equivalent to function properties. They cannot be used as contructors, for example.

const mathFunctions = {
add(a, b) {
return a + b;
},

subtract(a, b) {
return a - b;
},

multiply(a, b) {
return a * b;
},

divide(a, b) {
return a / b;
},
};

Returning a function from a function

A function can be returned from another function. The outer function is a type of higher-order function. This is common in currying and partial application.

function add(a) {
return function (b) {
return a + b;
};
}

const increment = add(1);
increment(1); // 2

const decrement = add(-1);
decrement(1); // 0

You are more likely to see this expressed as an arrow function.

const add = a => b => a + b;

Passing a function as an argument

A function can be passed to another function as an argument. The outer function is a type of higher-order function, while the inner function is known as a callback function. For example, the Array.prototype.forEach() method accepts a callback function.

const programmingLanguages = ["C#", "JavaScript", "Python"];

programmingLanguages.forEach((lang, index, languages) => {
console.log(`${lang} is at index ${index} of the array: ${languages}`);
});

Behind the scenes, the Array.prototype.forEach() method calls the callback function once for each element in the array. Here is a simplified version if its implementation. See Understanding callback functions in vanilla JS.

function forEach(array, callback) {
for (let i = 0; i < array.length; i++) {
callback(array[i], i, array);
}
}

const programmingLanguages = ["C#", "JavaScript", "Python"];

forEach(programmingLanguages, (lang, index, languages) => {
console.log(`${lang} is at index ${index} of the array: ${languages}`);
});

Adding a property to a function

Because JavaScript functions are objects of type Function, they can have properties and methods added to them.

function greet(name) {
return `Hello, ${name}!`;
}

greet.foo = "bar"; // Totally valid!

Inherited properties and methods

Most functions actually inherit a couple of properties from Function.prototype.

function greet(name) {
return `Hello, ${name}!`;
}

console.log(greet.length); // 1 (the function has 1 parameter)
console.log(greet.name); // "greet"

They also inherit a number of methods.

Here's an example of the Function.prototype.call() method, which calls a function with a given this value.

function getShapeInfo() {
return `A ${this.name} has ${this.sides} sides.`;
}

const shape = {
name: "square",
sides: 4,
};

const shapeInfo = getShapeInfo.call(shape);
console.log(shapeInfo); // "A square has 4 sides."

Static class members

Static class members are a common type of property added to a function. This is because classes in JavaScript are really just a special type of function.

class Point {
static distance(a, b) {
const dx = a.x - b.x;
const dy = a.y - b.y;
return Math.hypot(dx, dy);
}
}

console.log(typeof Point); // "function"
console.log(typeof Point.distance); // "function"

The preceding class declaration is equivalent to the following function declaration. Notice that the .distance() method is added to the Point function. That's right: a function property on a function!

function Point() {}

Point.distance = function (a, b) {
const dx = a.x - b.x;
const dy = a.y - b.y;
return Math.hypot(dx, dy);
};

console.log(typeof Point); // "function"
console.log(typeof Point.distance); // "function"

Instance methods

Most of the time, when a function is created, it is automatically assigned a .prototype property. When the function is invoked as a constructor, its .prototype property is used as the prototype of the object under construction. This is how inheritance works in JavaScript, it's just that the modern class syntax abstracts it away. For more information, see Inheritance and the prototype chain in the MDN Web Docs. Also see my post The .prototype property versus the [[Prototype]] internal slot in JavaScript.

class Point {
static distance(a, b) {
const dx = a.x - b.x;
const dy = a.y - b.y;
return Math.hypot(dx, dy);
}

constructor(x, y) {
this.x = x;
this.y = y;
}

toString() {
return `(${this.x}, ${this.y})`;
}
}

I've added an instance method, .toString(), to the preceding class declaration. The following is the equivalent function declaration. Note that the .toString() method is added to Point.prototype. The point (pun intended) is that Point, which is a function, has a .prototype property.

function Point(x, y) {
this.x = x;
this.y = y;
}

Point.distance = function (a, b) {
const dx = a.x - b.x;
const dy = a.y - b.y;
return Math.hypot(dx, dy);
};

Point.prototype.toString = function () {
return `(${this.x}, ${this.y})`;
};

In frameworks and libraries

It's common to see properties added to functions in frameworks and libraries. For example, the default export of the Express framework is the express() function. There are a couple of middleware functions that are properties of the express() function, e.g. .json() and .static().

const express = require("express");
const app = express();

app.use(express.json());
app.use(express.static("public"));

It may have fallen from grace in recent years, but jQuery is another great example. The jQuery() function (aliased as $) is used to construct a jQuery object for DOM traversal and manipulation.

const $div = $("div");
$div.parent().addClass("foo");

The jQuery() function has a number of properties and methods. For example, the jQuery.get() method is commonly used to make Ajax requests.

$.get("https://jsonplaceholder.typicode.com/users", function (users) {
console.log(users); // Array(10) [ {…}, {…}, {…}, {…}, {…}, … ]
});

Summary

JavaScript supports first-class functions. This means it treats them like any other value. They can be assigned to variables, stored in data structures, returned from other functions, and passed to other functions as arguments.

Hopefully this post has given you plenty of examples. I wrote it because of a time I struggled to explain callback functions to a group of students. I think an understanding of first-class functions would have helped.