Follow AiTechWorlds on LinkedIn for professional AI content!Follow Now →
20 minLesson 8 of 35
Modern ES6+ JavaScript

Arrow Functions & this Binding

Arrow Functions & this Binding

Arrow functions (=>) were introduced in ES6 and they're everywhere in modern JavaScript. But they're not just shorthand — they have a fundamentally different behavior around this that makes them essential to understand deeply.

Arrow Function Syntax

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

// Arrow function equivalents
const add = (a, b) => a + b;          // implicit return
const add = (a, b) => { return a + b; }; // explicit return with braces

// Single parameter — parentheses optional
const double = n => n * 2;
const double = (n) => n * 2;  // same thing

// No parameters — empty parentheses required
const sayHi = () => "Hello!";

// Returning an object — wrap in parentheses
const makeUser = (name, age) => ({ name, age });
// Without parens, the {} looks like a function body

Arrow Functions in Practice

const numbers = [1, 2, 3, 4, 5];

// Map
const doubled = numbers.map(n => n * 2);

// Filter + chain
const result = numbers
    .filter(n => n > 2)
    .map(n => n * n);

// Sort
const sorted = ["banana", "apple", "cherry"].sort((a, b) => a.localeCompare(b));

// Event handlers (in browser)
button.addEventListener("click", () => {
    console.log("Clicked!");
});

// Promise chains
fetch("/api/user")
    .then(res => res.json())
    .then(data => console.log(data))
    .catch(err => console.error(err));

The this Problem in Regular Functions

this in JavaScript refers to the execution context — who called the function. This causes notorious bugs:

const timer = {
    seconds: 0,
    
    start() {
        // setInterval calls this callback — but 'this' inside the callback
        // refers to the global object (or undefined in strict mode), NOT timer
        setInterval(function() {
            this.seconds++;  // BUG: this is wrong!
            console.log(this.seconds);
        }, 1000);
    }
};

This was worked around with var self = this or .bind(this):

start() {
    const self = this;  // capture 'this' before it changes
    setInterval(function() {
        self.seconds++;  // now it works
    }, 1000);
    
    // Or with .bind():
    setInterval(function() {
        this.seconds++;
    }.bind(this), 1000);
}

How Arrow Functions Fix this

Arrow functions don't have their own this. They inherit this from the surrounding scope (lexical this):

const timer = {
    seconds: 0,
    
    start() {
        // Arrow function uses the 'this' of start() — which is the timer object
        setInterval(() => {
            this.seconds++;  // works correctly!
            console.log(this.seconds);
        }, 1000);
    }
};

timer.start();  // logs 1, 2, 3...

When Arrow Functions Break

Arrow functions are NOT the right choice for object methods:

const user = {
    name: "Alice",
    
    // Arrow function as method — WRONG
    greet: () => {
        return `Hello, I'm ${this.name}`;  // 'this' is NOT user here!
        // In a browser: this is window (and window.name is "")
        // In Node.js strict mode: this is undefined
    },
    
    // Regular function as method — CORRECT
    greet() {
        return `Hello, I'm ${this.name}`;  // 'this' is user
    }
};

Also wrong for constructors and prototype methods:

// Arrow functions can't be constructors
const Person = (name) => { this.name = name; };
new Person("Alice");  // TypeError: Person is not a constructor

// Don't use arrow for prototype methods
MyClass.prototype.method = () => {
    this.value;  // 'this' is wrong
};

Practical Rule

Use arrow functions for:

  • Callbacks (setTimeout, map, filter, event handlers)
  • Short utility functions
  • Anywhere you want to preserve the outer this

Use regular functions for:

  • Object methods
  • Constructor functions (though classes are preferred now)
  • Functions that need their own this
class EventEmitter {
    constructor() {
        this.listeners = [];
        this.data = "some data";
    }
    
    // Regular function for the method itself
    on(callback) {
        this.listeners.push(callback);
    }
    
    // Regular function method that calls arrow function callbacks
    emit() {
        this.listeners.forEach(callback => {
            // Arrow function preserves 'this' from emit()
            callback(this.data);
        });
    }
}

call, apply, and bind

These three methods let you explicitly set this for regular functions:

function introduce(greeting, punctuation) {
    return `${greeting}, I'm ${this.name}${punctuation}`;
}

const alice = { name: "Alice" };
const bob = { name: "Bob" };

// call — pass arguments individually
introduce.call(alice, "Hello", "!");   // "Hello, I'm Alice!"
introduce.call(bob, "Hi", ".");        // "Hi, I'm Bob."

// apply — pass arguments as array
introduce.apply(alice, ["Hey", "~"]);  // "Hey, I'm Alice~"

// bind — returns a NEW function with this permanently set
const aliceIntro = introduce.bind(alice);
aliceIntro("Hello", "!");  // "Hello, I'm Alice!"

// bind with pre-filled arguments (partial application)
const formalAlice = introduce.bind(alice, "Good day", ".");
formalAlice();  // "Good day, I'm Alice."

Note: You cannot change this with call, apply, or bind on arrow functions — they ignore it.

Next lesson: Destructuring — extracting values from arrays and objects into variables cleanly.

📱

Get this course's notes on Telegram!

Free cheat sheets, summaries & practice exercises

Get Notes Free →
!