← Back to blog

Anonymous functions VS arrow functions in JavaScript

| JavaScript

This article was published over 2 years ago. Some information may be outdated.

Many JavaScript developers treat anonymous functions and arrow functions as interchangeable. They are not. This misunderstanding leads to real bugs.

Consider this Vue.js component:

<template>
    <div>
        <ul>
            <li v-for="user in users">{{ user.first_name }} {{ user.last_name }}
</li>
        </ul>
    </div>
</template>

<script>
export default {
    name: "Users",

    data() {
        return {
            users: [],
        }
    },

    mounted() {
        axios('https://reqres.in/api/users').then(response => function() {
            this.users = response.data.data;
        })
    }
}
</script>

The users array should be populated with response.data.data as soon as the component mounts. It will not. It will always remain an empty array.

The problem is that the this keyword does not get rebound inside anonymous functions. Let us inspect this to see what is happening:

mounted() {
    axios('https://reqres.in/api/users').then(function(response) {
        console.log(this);
    })
}

Output:

Window {window: Window, self: Window, document: document, name: "", location: Location, ...}

console.log(this) returns the Window object, not the Vue instance. The anonymous function creates its own this binding.

Before ES6, developers worked around this by capturing this in a variable:

const self = this;
axios('https://reqres.in/api/users').then(function(response) {
	console.log(self); // Vue instanse
})

Or by using the bind method:

axios('https://reqres.in/api/users').then(function(response) {
	console.log(this);
}.bind(this)); // Binds Vue instanse

Both work, but both are clumsy. ES6 arrow functions solve this cleanly.

Arrow functions inherit this from their enclosing scope. They do not create their own this binding.

axios('https://reqres.in/api/users').then(response => this.users = response.data.data);

Arrow functions also provide a shorter syntax:

const langs = ['PHP', 'JavaScript', 'Go', 'Python'];

// Short syntax, no need for parantheses and return statement. Only one line supported.
langs.map(lang => `I love ${lang} !`);

// Multiple arguments, you should use parantheses.
langs.map((item, key) => `${key} I love ${lang} !`);

// Multiple lines
langs.map((lang, key) => {
    // more lines goes here
    return `${key} I love ${lang} !`;
});

But arrow functions are not a drop-in replacement for anonymous functions. Here is why:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Events</title>

    <style>
        .style2 {
            background-color: coral;
            color: white;
            font-weight: bold;
        }
    </style>
</head>
<body>

<div id="app">
    Hello World
</div>

<script>
    const app = document.getElementById("app");

    app.addEventListener('click', function(event) {
        this.classList.toggle('style2');
    });
</script>
</body>
</html>

This works because the anonymous function receives this bound to the app element. Clicking the element toggles style2.

Now replace it with an arrow function:

app.addEventListener('click', () => {
    this.classList.toggle('mystyle');
});

Output:

Uncaught TypeError: Cannot read property 'toggle' of undefined ...

The arrow function inherits this from the enclosing scope, which is Window -- not the app element. The anonymous function is the correct choice here because addEventListener explicitly sets this to the target element, and only anonymous functions respect that binding.

Summary

  • Anonymous functions create their own this binding -- this is determined by how the function is called, not where it is defined.
  • Arrow functions inherit this from the enclosing scope -- they never create their own this binding.
  • Use arrow functions when you want to preserve the outer this, such as inside Vue methods, Promise callbacks, or class methods.
  • Use anonymous functions when you need this to be set by the caller, such as in addEventListener or object method definitions.
  • They are not interchangeable -- choosing the wrong one produces silent bugs that are difficult to trace.
Share