How do arrow functions differ from regular functions?
Advertisement
🧩 Scenario
Architecture Walkthrough
Lexical vs Dynamic this Binding
Regular functions create their own this binding at call time. The value of this depends on how the function is called, not where it is defined. A method called as a standalone function loses its original context, which is a frequent source of bugs when passing methods as callbacks.
Arrow functions do not create a this binding at all. They inherit this from the enclosing lexical scope at the time they are defined. This makes them predictable in asynchronous callbacks and class methods, because this will always refer to the object you expect.
Missing arguments, prototype, and new
Arrow functions do not have an arguments object. If you need to collect variadic arguments inside an arrow function, you must use rest parameters (...args). This is actually the preferred approach in modern JavaScript anyway, but it catches developers off guard when they expect arguments to exist.
Arrow functions also have no prototype property, which means they cannot be used with the new keyword. Attempting to call new on an arrow function throws a TypeError immediately. This is by design: because arrow functions have no this of their own, constructing an object around them would have no meaning.
When to Prefer a Regular Function
Arrow functions are not universally better. Object literals that use arrow functions for methods will have a broken this, since the surrounding lexical scope at definition time is the outer scope, not the object itself. DOM event handlers that need this to refer to the element that fired the event must use regular functions. Generator functions that use yield cannot be arrow functions at all.
Choosing between the two comes down to one question: do you need a new this binding, or do you want to capture the one you already have?
Key Code Explained
class Timer {
constructor() {
this.count = 0;
}
start() {
// Regular function: this is undefined in strict mode inside the callback
setInterval(function () {
this.count++; // TypeError: Cannot set properties of undefined
}, 1000);
}
startFixed() {
// Arrow function: this is captured from startFixed's scope (the Timer instance)
setInterval(() => {
this.count++;
console.log(this.count);
}, 1000);
}
}
The start method breaks because setInterval calls its callback as a plain function, not as a method of Timer. The regular function creates a new this binding that has no connection to the Timer instance.
The startFixed method works because the arrow function does not create a this binding. It looks up the scope chain and finds the this from startFixed, which is the Timer instance. This is what "lexical this" means in practice.
// arguments is not available in arrow functions
const regular = function () {
console.log(arguments[0]); // works
};
const arrow = (...args) => {
console.log(args[0]); // correct approach for arrow functions
};
Tradeoffs
| Approach | Pro | Con |
|---|---|---|
| Arrow function for callbacks | Predictable this from surrounding scope | Cannot be used as constructors or generators |
| Regular function for methods | Creates its own this, useful for object methods | Requires .bind(this) or a stored reference when used as callbacks |
| Regular function with .bind(this) | Explicit and readable in some codebases | Verbose and creates a new function on every call if done inside render |
What Interviewers Actually Check
- Whether you know that
thisin an arrow function is determined at definition time, not call time - Whether you can name at least two things arrow functions lack (arguments, prototype, new support)
- Whether you understand that arrow functions are not always the better choice
- Whether you can trace what
thisrefers to in a nested callback scenario - Whether you know the implicit return shorthand and when it applies
- Whether you can identify the bug in an arrow function used as an object method
Follow-Up Questions
- How does
thisbehave inside an arrow function defined inside a class field, compared to one defined inside a constructor method? - What happens if you nest an arrow function inside another arrow function? What does
thisresolve to? - How would you test that a callback correctly maintains the expected
thisbinding? - In a high-frequency event handler that runs thousands of times per second, does the choice of function type have any performance impact?
- A PM asks why a submitted form event listener is not updating the component state. You find the handler is an arrow function on a plain object. How do you explain and fix it?
Common Candidate Mistakes
- Using an arrow function as an object method and being confused when
thisrefers to the outer scope instead of the object - Trying to call
newon an arrow function and not knowing why it throws - Assuming
argumentsis available inside an arrow function - Returning an object literal from a single-expression arrow function without wrapping it in parentheses:
() => { foo: 1 }is a block with a label, not an object - Thinking
.bind()or.call()can overridethisinside an arrow function (they cannot)
Interview Readiness Checklist
Before you leave this question, make sure you can answer:
- Can you explain what lexical
thismeans and why it matters in callbacks? - Can you demonstrate the difference in
thisbehavior insidesetTimeoutusing both function types? - Can you explain why arrow functions cannot be used as constructors?
- Can you describe the missing
argumentsobject and how to work around it with rest parameters? - Can you identify at least two situations where a regular function is the correct choice over an arrow function?
Summary
Arrow functions and regular functions are not interchangeable. The most important distinction is this binding: regular functions create a new binding at call time, while arrow functions inherit this from the scope where they were defined. This makes arrow functions reliable for callbacks and class methods, but unreliable for object method definitions and DOM event handlers that need this to reference the element.
Beyond this, arrow functions also lack arguments, prototype, and the ability to be called with new. These restrictions are intentional. Arrow functions are designed to be lightweight, lexically transparent function expressions, not full replacements for the function declaration.
When choosing between the two, ask whether the code needs to create a new execution context or borrow one from its surroundings. That single question covers the vast majority of real-world decisions between arrow and regular functions.
Can arrow functions be used as constructors?
No. Arrow functions don't have their own this or prototype, so they can't be used as constructors.
Advertisement
Stay Updated
Get the latest frontend challenges, interview questions and tutorials delivered to your inbox.
Advertisement