🚀How JavaScript Works (Part 9)? Arrow functions and lexical this
Tags: webdev, javascript, programming, tutorial
Published: 2023-10-26 10:25:36

The advent of `ES6` brought us many exciting features, and among them, arrow functions stood out as a concise and elegant way to write functions. However, while arrow functions have been embraced for their brevity, they come with certain quirks that can be a double-edged sword for developers.

## Table of Contents
[The Anonymous Nature of Arrow Functions](#the-anonymous-nature-of-arrow-functions)
[The Inferred Purpose](#the-inferred-purpose)
[The Power of Descriptive Function Names](#the-power-of-descriptive-function-names)
[The Readability Factor](#the-readability-factor)
[Arrow Functions in the Context of this](#arrow-functions-in-the-context-of-this)
[The Essence of Lexical this](#the-essence-of-lexical-this)
[Arrow Functions and the new Keyword](#arrow-functions-and-the-new-keyword)
[The Curly Brace Confusion](#the-curly-brace-confusion)
[The Parent Lexical Scope](#the-parent-lexical-scope)
[The Right Tool for Lexical this Behavior](#the-right-tool-for-lexical-this-behavior)
[Conclusion](#conclusion)
[Sources](#sources)

## The Anonymous Nature of Arrow Functions

Arrow functions are inherently anonymous, unlike traditional functions with a distinct name. This anonymity can impact code readability, making it challenging for developers to quickly discern the purpose of a function. Consider this arrow function:

```javascript
const getId = people.map(person => person.id);
```

> As Kyle Simpson aptly put it, **"The only way to figure out what an arrow function is doing is to read its function body." **This lack of a name can obscure the function's intention, leading to potential confusion for you and other developers working on the code.

### The Inferred Purpose

Proponents of arrow functions argue that their purpose is self-evident. However, it's essential to remember that the purpose of an arrow function is inferred rather than explicitly expressed through a well-chosen name. You are required to deduce the function's intent by examining its code, which can sometimes lead to ambiguity.

### The Power of Descriptive Function Names

In contrast, when you use a named function, you provide a clear and explicit label for the function's purpose. For example, consider the alternative using a function declaration:

```javascript
function getIdFromPerson(person) {
  return person.id;
}
const ids = people.map(getIdFromPerson);
```
In this revised code, the function `getIdFromPerson` conveys its purpose without ambiguity. It's evident that this function retrieves the `id` property from a `person` object. Descriptive names enhance code readability and maintainability, making it easier for you and your team to understand and work with the codebase.

### The Readability Factor

While arrow functions offer brevity, they can sometimes compromise readability. Self-explanatory code, where the purpose of a function is evident from its name, often proves invaluable. Arrow functions' shorter syntax may initially appear more straightforward, but the brevity can come at the cost of understanding and maintainability. Moreover, arrow functions come in various syntax variations, leading to potential confusion in certain contexts.

## Arrow Functions in the Context of this

However, it's crucial to acknowledge that arrow functions bring a unique characteristic called "lexical this behaviour." This behaviour can be immensely valuable in specific situations.
Consider this example:

```javascript
const workshop = {
    topic: "JavaScript",
    ask: function () {
        setTimeout(() => {
            console.log(`Welcome to the ${this.topic} workshop!`);
        }, 1000);
    },
};
workshop.ask(); // Outputs: Welcome to the JavaScript workshop!
```
In this case, the `ask` method of the workshop object contains an arrow function passed to `setTimeout`. Surprisingly, within the arrow function, the `this` keyword correctly points to the `workshop` object. This phenomenon is the essence of **"lexical this behavior."**

Now, let's dive deeper into what **"lexical this"** means:

An arrow function does not define a `this` keyword. In fact, **it doesn't have a `this` keyword at all**; it treats this like any other variable.

## The Essence of Lexical this

In essence, **lexical this means that an arrow function will keep climbing up the scope chain until it locates a function with a defined `this` keyword**. The `this` keyword in the arrow function is determined by the function containing it. In the example, it looks up one level in scope and finds the ask function.

This understanding is crucial because it helps you avoid incorrect thinking, which can lead to bugs in your code. By thinking in alignment with JavaScript's design and the language specification (the spec), you can ensure that your code behaves as expected.

> **The JavaScript language specification confirms that an arrow function does not define local bindings for `arguments`, `super`, `this`, or `new.target`. This specification is a key point of reference, and adhering to it eliminates misconceptions and potential issues in your code.**

## Arrow Functions and the new Keyword
One fascinating aspect of arrow functions is their behavior concerning the `new` keyword. If you recall from earlier in this series, the `new` keyword takes precedence over a hardbound function. That means, for some unusual reason, if you call `new` on a hardbound function, it can override the hard binding and become the new `object`. However, this is not the case with arrow functions. Calling `new` on an arrow function results in an exception, as arrow functions are not hardbound functions.
> `"TypeError: function is not a constructor"`

Understanding this distinction is vital for maintaining code clarity and preventing unexpected behavior in edge cases.

## The Curly Brace Confusion

One of the persistent frustrations among developers is the assumption that curly braces `{}` imply a scope. We often associate them with `blocks`, `function` bodies, or scopes. However, it's essential to understand that not every set of curly braces denotes a scope.
Consider this example:

```javascript
const workshop = {
    teacher: "Kyle",
    ask: (question) => {
        console.log(this.teacher, question} 
    },
};
```
At first glance, it appears that this function is enclosed in a scope. Still, this is not the case. These curly braces merely define the function body; they don't create a scope. Arrow functions don't introduce a new scope like regular functions do.

## The Parent Lexical Scope

One common misconception is that arrow functions should inherit the scope in which they are defined. For example, you might expect an arrow function defined within an object to capture that object as its `this` context. However, this isn't the case.

Arrow functions are not context-aware; they don't possess their own `this`. Instead, they resolve `this` lexically, which means they inherit the `this` from their parent lexical scope. In the global scope, this would typically be the global object.

For example, in the workshop object:

```javascript
workshop.ask("What happened to 'this'?")
// undefined What happened to 'this'?
```
The `this` inside the arrow function does not point to the `workshop` object but inherits the `this` from the global scope. So, it logs `undefined`.

## The Right Tool for Lexical this Behavior

Arrow functions are not a one-size-fits-all solution; they are the right tool for maintaining the `this` context from their parent scope. For instance, when you need to pass a function as a callback to methods like `setTimeout`, using an arrow function ensures that `this` remains consistent with the `context` in which it's defined.
Let's consider a scenario:

```javascript
workshop.ask.call(workshop, "Still no 'this'");
// undefined Still no 'this'
```
Here, we explicitly use `call` to set the `this` context to the `workshop` object. However, the arrow function still resolves `this` lexically, ignoring the provided context. It logs `undefined`.

## Conclusion

The key takeaway here is that arrow functions are not a one-size-fits-all solution. While they excel in certain situations, such as when you require **lexical this** behaviour, they may not be the best choice for every use case. It's crucial to understand their behaviour and limitations fully. By doing so, you can wield arrow functions effectively, leveraging their strengths while avoiding their potential pitfalls. Ultimately, mastering this powerful feature will help you become a more proficient and versatile JavaScript developer.

## Sources
**Kyle Simpson's "You Don't Know JS"**
**MDN** Web Docs - The Mozilla Developer Network (MDN)