Skip to main content
โšก Calmops

Arrow Functions and Function Expressions

Arrow Functions and Function Expressions

Arrow functions and function expressions are modern ways to define functions in JavaScript. Understanding their differences is important.

Function Expressions

A function expression assigns a function to a variable:

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

console.log(add(2, 3)); // 5

Named Function Expressions

const factorial = function fact(n) {
    if (n <= 1) return 1;
    return n * fact(n - 1);
};

console.log(factorial(5)); // 120

Anonymous Function Expressions

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

console.log(greet("Alice")); // "Hello, Alice!"

Arrow Functions

Arrow functions provide concise syntax:

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

console.log(add(2, 3)); // 5

Concise Syntax

For single expressions, omit braces and return:

const add = (a, b) => a + b;
console.log(add(2, 3)); // 5

const square = x => x * x;
console.log(square(4)); // 16

const greet = () => "Hello!";
console.log(greet()); // "Hello!"

Multiple Parameters

// Multiple parameters need parentheses
const multiply = (a, b) => a * b;

// Single parameter - parentheses optional
const double = x => x * 2;
const double2 = (x) => x * 2; // Also valid

// No parameters - parentheses required
const random = () => Math.random();

Multiple Statements

const calculate = (a, b) => {
    const sum = a + b;
    const product = a * b;
    return { sum, product };
};

console.log(calculate(3, 4)); // { sum: 7, product: 12 }

Returning Objects

// Wrong - looks like code block
// const createUser = name => { name: name };

// Correct - wrap in parentheses
const createUser = name => ({ name });
console.log(createUser("Alice")); // { name: "Alice" }

Key Differences

1. this Binding

Arrow functions don’t have their own this:

const person = {
    name: "Alice",
    greet: function() {
        console.log(this.name); // "Alice"
    },
    greetArrow: () => {
        console.log(this.name); // undefined (this is global)
    }
};

person.greet(); // "Alice"
person.greetArrow(); // undefined

2. arguments Object

Arrow functions don’t have arguments:

const regularFunc = function() {
    console.log(arguments); // [1, 2, 3]
};

const arrowFunc = () => {
    console.log(arguments); // ReferenceError
};

regularFunc(1, 2, 3);

Use rest parameters instead:

const arrowFunc = (...args) => {
    console.log(args); // [1, 2, 3]
};

arrowFunc(1, 2, 3);

3. new Keyword

Arrow functions can’t be used as constructors:

const RegularFunc = function(name) {
    this.name = name;
};

const user = new RegularFunc("Alice"); // Works

const ArrowFunc = (name) => {
    this.name = name;
};

const user2 = new ArrowFunc("Bob"); // TypeError

4. Hoisting

Function expressions are not hoisted:

console.log(add(2, 3)); // TypeError: add is not a function

const add = (a, b) => a + b;

Function declarations are hoisted:

console.log(add(2, 3)); // 5

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

When to Use Each

Use Arrow Functions

For callbacks and simple operations:

// Good - arrow function for callback
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(x => x * 2);

// Good - arrow function for simple operation
const add = (a, b) => a + b;

Use Function Expressions

When you need this binding:

const button = document.getElementById("myButton");

button.addEventListener("click", function() {
    console.log(this); // button element
});

// Wrong - arrow function loses this
button.addEventListener("click", () => {
    console.log(this); // window object
});

Use Function Declarations

For main functions and methods:

// Good - function declaration
function calculateTotal(items) {
    return items.reduce((sum, item) => sum + item.price, 0);
}

// Good - method in object
const calculator = {
    add: function(a, b) {
        return a + b;
    }
};

Practical Examples

Array Methods

const users = [
    { id: 1, name: "Alice", age: 30 },
    { id: 2, name: "Bob", age: 25 },
    { id: 3, name: "Charlie", age: 35 }
];

// map
const names = users.map(user => user.name);
console.log(names); // ["Alice", "Bob", "Charlie"]

// filter
const adults = users.filter(user => user.age >= 30);
console.log(adults); // [Alice, Charlie]

// reduce
const totalAge = users.reduce((sum, user) => sum + user.age, 0);
console.log(totalAge); // 90

Event Handlers

// Function expression - can use this
const button = {
    element: document.getElementById("btn"),
    init: function() {
        this.element.addEventListener("click", function() {
            console.log(this); // button element
        });
    }
};

// Arrow function - loses this
const button2 = {
    element: document.getElementById("btn"),
    init: function() {
        this.element.addEventListener("click", () => {
            console.log(this); // button2 object
        });
    }
};

Promises and Async

// Arrow functions work well with promises
fetch("/api/users")
    .then(response => response.json())
    .then(data => console.log(data))
    .catch(error => console.error(error));

// Async arrow function
const fetchUsers = async () => {
    try {
        const response = await fetch("/api/users");
        return await response.json();
    } catch (error) {
        console.error(error);
    }
};

Currying

// Arrow functions for currying
const add = a => b => a + b;
const add5 = add(5);
console.log(add5(3)); // 8

// Multiply function
const multiply = a => b => a * b;
const double = multiply(2);
console.log(double(5)); // 10

Composition

const compose = (f, g) => x => f(g(x));

const addOne = x => x + 1;
const double = x => x * 2;

const addOneThenDouble = compose(double, addOne);
console.log(addOneThenDouble(5)); // 12 (5 + 1 = 6, 6 * 2 = 12)

Comparison Table

Feature Function Declaration Function Expression Arrow Function
Syntax function name() {} const name = function() {} const name = () => {}
Hoisting Yes No No
this binding Own Own Lexical
arguments Yes Yes No (use rest)
Constructor Yes Yes No
Concise syntax No No Yes
Best for Main functions Callbacks Simple operations

Best Practices

Use Arrow Functions for Callbacks

// Good
const numbers = [1, 2, 3];
const doubled = numbers.map(x => x * 2);

// Avoid
const doubled2 = numbers.map(function(x) {
    return x * 2;
});

Use Function Expressions for Methods

// Good
const obj = {
    name: "Alice",
    greet: function() {
        return `Hello, ${this.name}!`;
    }
};

// Avoid
const obj2 = {
    name: "Bob",
    greet: () => {
        return `Hello, ${this.name}!`; // this is wrong
    }
};

Be Consistent

// Good - consistent style
const users = [
    { id: 1, name: "Alice" },
    { id: 2, name: "Bob" }
];

const names = users.map(user => user.name);
const ids = users.map(user => user.id);

// Avoid - mixing styles
const names2 = users.map(function(user) { return user.name; });
const ids2 = users.map(user => user.id);

Summary

  • Function expressions: assign function to variable
  • Arrow functions: concise syntax with =>
  • Arrow functions: lexical this binding
  • Arrow functions: no arguments object
  • Arrow functions: can’t be constructors
  • Use arrow functions: for callbacks, simple operations
  • Use function expressions: when you need this
  • Use function declarations: for main functions

Official Documentation

Next Steps

  1. The ’this’ Keyword and Context Binding
  2. Array Methods: map, filter, reduce, forEach
  3. Callbacks and Asynchronous JavaScript

Comments