JavaScript Arrow Functions


JavaScript Arrow Functions Interview with follow-up questions

1. What is an arrow function in JavaScript?

An arrow function is a compact syntax for function expressions, introduced in ES2015 (ES6). Beyond brevity, it differs from regular functions in a few semantic ways that interviewers care about more than the shorthand.

const add = (a, b) => a + b;     // implicit return
const square = n => n * n;        // single param, no parens needed
const greet = () => ({ ok: true }); // wrap object literal in parens

What makes arrows distinct (not just shorter):

  • No own this — they close over this from the enclosing lexical scope. Great for callbacks; bad as object/prototype methods that need dynamic this.
  • No own arguments — use rest params (...args) instead.
  • Can't be used with new — they have no [[Construct]] and no prototype.
  • No own super or new.target.

So the right framing is: arrows are the default for inline callbacks (map, setTimeout, Promise handlers) precisely because they inherit this, and you fall back to regular functions/method shorthand when you need a dynamic this.

↑ Back to top

Follow-up 1

How does it differ from a regular function?

Arrow functions differ from regular functions in a few ways:

  1. Syntax: Arrow functions have a shorter syntax compared to regular functions.
  2. Binding of 'this': Arrow functions do not bind their own 'this' value, instead, they inherit the 'this' value from the enclosing lexical scope.
  3. No 'arguments' object: Arrow functions do not have their own 'arguments' object, so you cannot access the arguments passed to the function using 'arguments' keyword.

Follow-up 2

Can you provide an example of an arrow function?

Sure! Here's an example of an arrow function that adds two numbers:

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

In this example, the arrow function add takes two parameters a and b and returns their sum.

Follow-up 3

What are the benefits of using arrow functions?

There are several benefits of using arrow functions:

  1. Concise syntax: Arrow functions have a shorter and more concise syntax compared to regular functions, making the code easier to read and write.
  2. Lexical 'this' binding: Arrow functions do not bind their own 'this' value, which means they inherit the 'this' value from the surrounding scope. This can help avoid confusion and simplify the code.
  3. No 'arguments' object: Arrow functions do not have their own 'arguments' object, which can lead to cleaner and more predictable code.
  4. Implicit return: If the arrow function has a single expression, it will be implicitly returned without the need for the return keyword.

Follow-up 4

In what scenarios would you use an arrow function?

Arrow functions are commonly used in the following scenarios:

  1. Callback functions: Arrow functions are often used as callback functions, especially in scenarios where the 'this' value needs to be preserved.
  2. Iteration methods: Arrow functions are frequently used with iteration methods like map, filter, and reduce to provide concise and readable code.
  3. Event handlers: Arrow functions are useful for defining event handlers, as they automatically inherit the 'this' value from the surrounding scope.
  4. Shorter function expressions: Arrow functions can be used to write shorter and more concise function expressions in general.

2. How does 'this' behave in arrow functions?

Arrow functions have no own this. Instead, this is resolved lexically — it's whatever this is in the scope where the arrow was defined, and it never changes based on how the arrow is called. This is the single most-tested fact about arrows.

class Timer {
  seconds = 0;
  start() {
    setInterval(() => {
      this.seconds++;        // `this` = the Timer instance (lexical)
    }, 1000);
  }
}

A regular function callback here would lose this (it'd be undefined or the global object), which is exactly the bug arrows solve.

Consequences interviewers probe:

  • call/apply/bind can't change an arrow's this — the this argument is ignored.
  • Don't use arrows as object methods when you need this to be the object:
const obj = {
  name: 'x',
  bad: () => this.name,   // `this` is NOT obj (lexical, outer scope)
  good() { return this.name; }, // method shorthand — `this` is obj
};
  • Same reason they're poor for prototype methods and DOM handlers that need the element via this.
↑ Back to top

Follow-up 1

How is it different from 'this' in regular functions?

In regular functions, the value of 'this' is determined by how the function is called. It can vary depending on the context in which the function is invoked.

Follow-up 2

Can you provide an example where 'this' behaves differently in arrow functions and regular functions?

Sure! Here's an example:

const obj = {
  name: 'John',
  regularFunc: function() {
    console.log(this.name);
  },
  arrowFunc: () => {
    console.log(this.name);
  }
};

obj.regularFunc(); // Output: 'John'
obj.arrowFunc(); // Output: undefined

In this example, the 'regularFunc' is a regular function and 'arrowFunc' is an arrow function. When 'regularFunc' is called, 'this' refers to the 'obj' object, so it outputs 'John'. However, when 'arrowFunc' is called, 'this' refers to the enclosing scope (which is the global scope in this case), so it outputs 'undefined'.

Follow-up 3

What are the implications of this difference in behavior?

The difference in behavior of 'this' in arrow functions and regular functions can have implications when it comes to object methods, event handlers, and callback functions. Arrow functions are useful when you want to preserve the value of 'this' from the surrounding context, while regular functions allow 'this' to be dynamically determined based on how the function is called.

3. Can arrow functions be used as constructors?

No — arrow functions cannot be used as constructors. Calling one with new throws a TypeError.

const Person = (name) => { this.name = name; };
const p = new Person('Ada'); // TypeError: Person is not a constructor

Why, and what an interviewer wants you to explain:

  • Arrow functions have no internal [[Construct]] method, so new has nothing to invoke.
  • They have no prototype property, so there's no object for a new instance to inherit from.
  • They have no own thisnew works by binding a fresh this, but an arrow's this is fixed lexically and can't be rebound.
  • They also lack their own arguments, super, and new.target.

When you need a constructor, use a class (the modern choice) or a regular function:

class Person {
  constructor(name) { this.name = name; }
}
↑ Back to top

Follow-up 1

Why or why not?

Arrow functions do not have their own 'this' value. They inherit the 'this' value from the enclosing lexical scope. This means that the 'this' value inside an arrow function is the same as the 'this' value outside the arrow function. Constructors, on the other hand, require the use of the 'new' keyword to create a new instance of an object and set the 'this' value to the newly created object. Since arrow functions do not have their own 'this' value, they cannot be used as constructors.

Follow-up 2

What happens if you try to use an arrow function as a constructor?

If you try to use an arrow function as a constructor by calling it with the 'new' keyword, a TypeError will be thrown. This is because arrow functions do not have a prototype property, which is necessary for creating new instances of objects.

Follow-up 3

Can you provide an example?

Sure! Here's an example that demonstrates the use of an arrow function as a constructor:

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

const person = new Person('John'); // Throws a TypeError

In this example, the arrow function 'Person' is intended to be used as a constructor for creating instances of a 'Person' object. However, when we try to create a new instance of 'Person' using the 'new' keyword, a TypeError is thrown because arrow functions cannot be used as constructors.

4. Can you explain the concept of lexical scoping in the context of arrow functions?

Lexical scoping means a variable's scope is determined by where it's written in the source, not by where or how it's called. Inner functions can reach variables of the enclosing scopes, resolved at definition time.

Arrow functions follow normal lexical scoping for variables — but the notable thing is that they also resolve this, arguments, super, and new.target lexically, since they have none of their own. A regular function gets a fresh this/arguments per call; an arrow simply borrows them from the surrounding scope.

function group(items) {
  const tag = 'item';
  return items.map(x => `${tag}:${x}`); // arrow closes over `tag` lexically
}

And the this consequence — the part interviewers are really testing:

const obj = {
  values: [1, 2, 3],
  factor: 10,
  scale() {
    // arrow inherits `this` from scale(), so this.factor works
    return this.values.map(v => v * this.factor);
  },
};
obj.scale(); // [10, 20, 30]

So lexical scoping is why arrows are ideal as callbacks: they "see" the variables and the this of the place they were written, with no this-rebinding surprises.

↑ Back to top

Follow-up 1

How does it differ from dynamic scoping?

Dynamic scoping is a different scoping mechanism where the scope of a variable is determined by the current execution context, rather than the location in the source code. In dynamic scoping, the variables used inside a function are resolved in the scope where the function is called, rather than where it is defined. This can lead to unexpected behavior and makes the code harder to reason about.

Follow-up 2

Can you provide an example of lexical scoping with an arrow function?

Sure! Here's an example:

const outerFunction = () => {
  const message = 'Hello';
  const innerFunction = () => {
    console.log(message);
  };
  innerFunction();
};

outerFunction();

In this example, the innerFunction is an arrow function that is defined inside the outerFunction. It has access to the message variable from its surrounding scope, which is the outerFunction. When innerFunction is called, it will log 'Hello' to the console.

Follow-up 3

What are the benefits of lexical scoping?

Lexical scoping offers several benefits:

  1. Simpler code: Lexical scoping allows for cleaner and more readable code by reducing the need for global variables and explicit parameter passing.
  2. Closures: Lexical scoping enables the creation of closures, which are functions that have access to variables from their surrounding scope even after the outer function has finished executing.
  3. Code reusability: Lexical scoping allows functions to access variables from their surrounding scope, making it easier to reuse and compose functions.
  4. Improved performance: Lexical scoping can improve performance by reducing the need for variable lookups in higher scopes.

5. How do you handle methods that require their own 'this' value in an object when using arrow functions?

Because an arrow function takes this lexically from its surrounding scope, it's the wrong choice for an object method that needs this to be the object itself. The fix is to use a method that has its own dynamic thismethod shorthand or a regular function:

const obj = {
  name: 'Widget',
  // BAD: arrow's `this` is the outer scope, not `obj`
  badGreet: () => `Hi from ${this?.name}`, // undefined

  // GOOD: method shorthand has its own `this`
  greet() { return `Hi from ${this.name}`; },
};

obj.greet(); // "Hi from Widget"

Related points interviewers expect:

  • Mixing the two on purpose: a regular method can contain an arrow so the inner callback inherits the method's this — the common pattern for map/setTimeout/event callbacks inside a method.
  • bind, call, apply set this for regular functions but are ignored by arrows.
  • For class methods, declare them normally; if you need an auto-bound handler (e.g. passing a method as a callback), use a class field with an arrow so this stays the instance:
class Button {
  label = 'OK';
  onClick = () => console.log(this.label); // bound to instance
}
↑ Back to top

Follow-up 1

Can you provide an example?

Sure! Here's an example that demonstrates the issue with using arrow functions as methods in objects:

const obj = {
  name: 'John',
  sayHello: () => {
    console.log('Hello, ' + this.name);
  }
};

obj.sayHello(); // Output: Hello, undefined

In this example, the arrow function sayHello is defined within the obj object. However, when sayHello is called, the 'this' value inside the arrow function does not refer to the obj object, resulting in undefined.

Follow-up 2

What are the alternatives to handle this scenario?

There are a few alternatives to handle the scenario of methods requiring their own 'this' value in an object when using arrow functions:

  1. Use regular function expressions: Instead of using arrow functions, you can define the methods as regular function expressions. This way, each method will have its own 'this' value that refers to the object itself.

  2. Use bind(): You can use the bind() method to bind the 'this' value of the arrow function to the object. This creates a new function with the specified 'this' value.

  3. Use class methods: If you are working with classes, you can define the methods as class methods using the method syntax. Class methods automatically bind the 'this' value to the object instance.

Follow-up 3

What are the implications of using arrow functions in this scenario?

When using arrow functions as methods in objects, the 'this' value will not refer to the object itself. Instead, it will be determined by the surrounding context where the arrow function is defined. This can lead to unexpected behavior and bugs if the arrow function relies on the 'this' value being the object. It is important to be aware of this behavior and choose the appropriate function type (arrow function or regular function expression) based on the desired 'this' binding.

Live mock interview

Mock interview: JavaScript Arrow Functions

Intermediate ~5 min Your own free AI key

Your voice and your AI key never touch our servers; the key stays in this browser and is sent only to Google. Only your round scores are saved to track progress.