Core Concepts9 min read ยท Updated 2026-03-10

Arrow Functions vs Regular Functions in JavaScript: 6 Key Differences

Arrow functions aren't just shorter syntax. They have fundamentally different behavior around this, arguments, constructors, and generators. Knowing when each applies makes the difference between code that works and code that subtly breaks.

๐Ÿ’ก Practice these concepts interactively with AI feedback

Start Practicing โ†’

Arrow functions look like a shorthand for function expressions, and for simple callbacks they are exactly that. But they have six behavioral differences that matter โ€” and using an arrow function where a regular function is required is one of the most common sources of this-related bugs.

The Six Differences

### 1. this Binding โ€” The Big One

Regular functions create their own this at call time, determined by how they are called. Arrow functions have no this of their own โ€” they inherit this from the lexical scope where they were defined.

const user = {
  name: 'Alice',

// Regular function: 'this' is the object at call time greetRegular: function() { return Hello, I'm ${this.name} },

// Arrow function: 'this' inherited from where the arrow was written // At definition time, the object literal isn't a scope โ€” the outer scope is greetArrow: () => { return Hello, I'm ${this.name} }, }

user.greetRegular() // "Hello, I'm Alice" โ€” this = user โœ“ user.greetArrow() // "Hello, I'm undefined" โ€” this = outer scope โœ—

The arrow function was written inside an object literal, but object literals don't create a new scope. So this goes to the surrounding scope โ€” in a browser, that's window (or undefined in strict mode). Neither has a name property.

Where arrow functions shine โ€” preserving this in callbacks:

class Timer {
  constructor() {
    this.seconds = 0
  }

start() { // Regular function: 'this' is undefined in strict mode (or window in sloppy) // setInterval(function() { this.seconds++ }, 1000) // broken

// Arrow function: inherits 'this' from start() โ€” the Timer instance setInterval(() => { this.seconds++ console.log(this.seconds) }, 1000) } }

const t = new Timer() t.start() // 1, 2, 3... โ€” works correctly because of arrow's lexical this

Before arrow functions, the standard fix was const self = this or .bind(this). Arrow functions made this unnecessary.

### 2. arguments Object

Regular functions have an implicit arguments object โ€” an array-like containing all arguments passed to the function. Arrow functions do not have arguments โ€” if you reference arguments inside an arrow function, you get the arguments of the nearest enclosing regular function.

function regular() {
  console.log(arguments)  // Arguments [1, 2, 3]
}
regular(1, 2, 3)

const arrow = () => { console.log(arguments) // ReferenceError (or outer function's arguments) } arrow(1, 2, 3)

In modern code, use rest parameters instead โ€” they work in both, are a real Array (with .map(), .filter()), and are more explicit:

const arrow = (...args) => {
  console.log(args)  // [1, 2, 3] โ€” a real Array
}
arrow(1, 2, 3)

### 3. Constructor Usage

Regular functions can be used as constructors with new. Arrow functions cannot โ€” they throw a TypeError.

function Person(name) {
  this.name = name
}
const alice = new Person('Alice')  // { name: 'Alice' } โœ“

const Animal = (name) => { this.name = name } const cat = new Animal('Cat') // TypeError: Animal is not a constructor โœ—

Arrow functions don't have a prototype property and don't have their own this, so they can't function as constructors. If you need a constructor, use a regular function or a class.

### 4. prototype Property

Regular functions automatically get a prototype property (used by the prototype chain when called with new). Arrow functions do not.

function Fn() {}
Fn.prototype  // { constructor: Fn }

const ArrowFn = () => {} ArrowFn.prototype // undefined

This is consistent with arrow functions not being constructors โ€” without prototype, there's no way to set up the prototype chain for new-created instances.

### 5. Generator Functions

Regular functions can be generator functions using function*. Arrow functions cannot be generators.

function* generator() {
  yield 1
  yield 2
  yield 3
}

const arrowGen = *() => { ... } // SyntaxError โ€” not supported

If you need a generator, use function*.

### 6. new.target

Regular functions can check new.target to determine if they were called with new. Arrow functions don't have new.target โ€” they inherit it from the enclosing scope.

function Foo() {
  if (!new.target) {
    return new Foo()  // Called without new โ€” redirect
  }
  this.x = 1
}

new Foo() // { x: 1 } Foo() // also { x: 1 } โ€” redirected

Decision Matrix

| Use case | Regular function | Arrow function | |---|---|---| | Object method | โœ“ | โœ— (loses object's this) | | Class method | โœ“ | โœ“ (as class field โ€” binds to instance) | | Constructor | โœ“ | โœ— | | Prototype method | โœ“ | โœ— | | Array callback (map, filter) | โœ“ | โœ“ | | setTimeout / setInterval callback | โœ“ (with bind) | โœ“ (lexical this) | | Event handler (needs this as element) | โœ“ | โœ— | | Generator | โœ“ | โœ— | | Needs arguments object | โœ“ | โœ— |

Where Arrow Functions Are Always Better

Array callbacks โ€” you almost never need this or arguments here, and shorter syntax is cleaner:

const result = items
  .filter(item => item.active)
  .map(item => item.name)
  .reduce((acc, name) => ({ ...acc, [name]: true }), {})

Callbacks that need to preserve outer this:

class Component {
  handleClick() {
    fetch('/api/data')
      .then(res => res.json())
      .then(data => {
        this.setState(data)  // arrow inherits 'this' from handleClick โ€” the component
      })
  }
}

Single-expression functions:

const double = n => n * 2
const isEven = n => n % 2 === 0
const getKey = obj => obj.id

Class Field Arrow Functions โ€” A Special Case

Arrow functions as class fields create instance-specific methods โ€” each instance gets its own copy of the function, with this permanently bound to that instance.

class Button {
  // Arrow class field โ€” creates a new function per instance
  // 'this' is always the instance, even when used as a callback
  handleClick = () => {
    console.log(this.label)
  }

constructor(label) { this.label = label } }

const btn = new Button('Submit') const { handleClick } = btn // extract it handleClick() // "Submit" โ€” 'this' is permanently bound to btn

This is common in React class components and event handler patterns. The cost: each instance gets its own copy of the function (vs prototype methods which are shared). For most components this is negligible.

The Rule

Use arrow functions for:

Use regular functions for:

When in doubt: if you're writing a method that belongs to an object or class, use a regular function or method syntax. If you're writing a callback, use an arrow function.

๐Ÿ“š Practice These Topics
This keyword
6โ€“10 questions
Classes
5โ€“8 questions
Closure
8โ€“12 questions
Arrow function
4โ€“7 questions

Put This Into Practice

Reading articles is passive. JSPrep Pro makes you actively recall, predict output, and get AI feedback.

Start Free โ†’Browse All Questions

Related Articles

Core Concepts
map() vs forEach() in JavaScript: Which One to Use and Why It Matters
7 min read
Interview Prep
Promise.all vs allSettled vs race vs any: The Complete Comparison
9 min read
Core Concepts
null vs undefined in JavaScript: What They Mean, When They Appear, and How to Handle Each
7 min read