🚀How JavaScript Works (Part 7)? `this`
Tags: webdev, javascript, tutorial, programming
Published: 2023-10-26 10:21:19

The `this` keyword in JavaScript has long been a source of confusion and frustration for many developers. It often behaves in ways that seem unintuitive, leading to a sense of bewilderment. However, this is an incredibly powerful mechanism that allows for dynamic context switching in your code. To master it, we need to understand how it works in JavaScript, free from preconceived notions based on other programming languages.

## Table of Contents
[Lexical Scope vs. Dynamic Scope](#lexical-scope-vs-dynamic-scope)
[The Pitfall of Comparisons](#the-pitfall-of-comparisons)
[The Basics: `this` Represents the Execution Context](#basics)
[Flexibility and Reusability through `this`](#flexibility)
[Explicitly Setting the Context](#explicitly-setting-the-context)
[The Four Ways to Invoke Functions](#the-four-ways-to-invoke-functions)
[Conceptualizing Different Buildings](#conceptualizing-different-buildings)
[Conclusion](#conclusion)
[Sources](#sources)

## Lexical Scope vs. Dynamic Scope

Before diving into function invocation, it's crucial to understand two fundamental concepts related to JavaScript's scoping: lexical scope and dynamic scope.

### Defining Lexical Scope:
Lexical scope, also known as static scope, refers to the idea that scopes are nested within each other. In this model, a compiler, parser, or processor determines all scopes during the compilation phase, well before the code is executed. This means that the structure of scopes and variable access is decided at author time, during the development of the code.

- **Compile-Time Decisions:**
Lexical scope is closely related to compile time. When we talk about lexical scope, it means that decisions about variable access, function nesting, and scope hierarchy are made at compile time. These decisions are not influenced by what happens at runtime during program execution.

- **Fixed and Predictable Scope:**
The variable references in lexical scope are determined during compilation and do not change during runtime. This means that, at compile time, it's known which variables belong to which scopes.

- **Optimizability:**
Lexical scope is highly optimizable because variable access is determined at compile time. There's no need for the runtime environment to continually look up variable references. Even in cases where variables may not be known at compile time, they can be resolved during the initial execution and remain consistent thereafter.

It's important to note that, despite some nuances, JavaScript fundamentally operates with lexical scope.

## Dynamic Scope

Dynamic scope, in contrast, allows runtime conditions to affect scope and variable access. While this model is rare in JavaScript, it's more common in languages like Bash scripting. Dynamic scope offers flexibility, but it can be challenging for developers accustomed to lexical scope.

- **Flexibility of Dynamic Scope:**
Dynamic scope allows a function to have different variable resolutions when called from different places. This flexibility can be seen as both powerful and potentially chaotic. While it may seem unfamiliar to developers accustomed to lexical scope, it offers the potential for extensive code reuse.

In JavaScript, you can achieve a level of flexibility similar to dynamic scope using various `this` binding techniques, as we'll see shortly.

> **They are simply different models for scoping. Lexical scope is fixed and predictable, determined at author time during compilation, while dynamic scope is runtime-dependent and flexible.**

## The Pitfall of Comparisons

One common mistake is trying to relate JavaScript's `this` to the behavior of this in other languages. This can lead to confusion and hinder your ability to understand and utilize JavaScript effectively. It's essential to approach `this` as a unique JavaScript feature.

## The Basics: `this` Represents the Execution Context <a name="basics"></a>

The `this` keyword in JavaScript represents the execution context for a function. Unlike many other languages where `this` refers to the context of the function's definition, in JavaScript, it's **all about how the function is called**. It's not determined by where a function is defined but by how it's called This means that this is dynamic and is determined by the context of the function call.

Here's a simple example to illustrate this point:

```javascript
function sayHello() {
    console.log(`Hello, ${this.name}!`);
}

const person1 = { name: "Alice", sayHello };
const person2 = { name: "Bob", sayHello };

person1.sayHello(); // Outputs: Hello, Alice!
person2.sayHello(); // Outputs: Hello, Bob!
```
In this example, the `sayHello` function's `this` context dynamically changes based on the object from which it is called.

## Flexibility and Reusability through `this` <a name="flexibility"></a>

JavaScript's `this` behavior provides a level of flexibility and reusability in your code that might seem counterintuitive at first. By changing the context of a function when invoking it, you can reuse the same function with different objects or settings.

For instance:

```javascript
const teacher1 = { name: "Alice" };
const teacher2 = { name: "Bob" };

function introduce() {
    console.log(`I am ${this.name}, your teacher.`);
}

introduce.call(teacher1); // Outputs: I am Alice, your teacher.
introduce.call(teacher2); // Outputs: I am Bob, your teacher.
```
The `call` method allows you to alter the context in which the `introduce` function operates. This dynamic flexibility can be incredibly powerful in real-world JavaScript applications.


## Explicitly Setting the Context
In JavaScript, you can explicitly set the context in which a function operates using methods like `call`, `apply`, and `bind`. Each of these methods allows you to change the `this` context for a given function.

Consider these examples:

```javascript
const person = { name: "Alice" };

function greet(greeting) {
    console.log(`${greeting}, ${this.name}!`);
}

greet.call(person, "Hello"); // Outputs: Hello, Alice!
greet.apply(person, ["Hi"]);  // Outputs: Hi, Alice!

const greetToAlice = greet.bind(person);
greetToAlice("Hey");  // Outputs: Hey, Alice!
```
`call` and `apply` are used for immediate function invocation, and you can pass `arguments` directly.
`bind` creates a new function with the specified context, which can be invoked later.

## The Four Ways to Invoke Functions

In JavaScript, there are four primary ways to invoke functions, each influencing how the `this` keyword behaves:

- **Function Invocation:** Invoking a function as `someFunction()` uses the global context.
- **Method Invocation:** Invoking a method as `object.someMethod()` sets `this` to the `object`.
- **Constructor Invocation:** Using `new SomeConstructor()` assigns `this` to the new instance created by the `constructor`.
- **Indirect Invocation:** Employing `someFunction.call(context, args)` specifies the context explicitly.

## Conceptualizing Different Buildings

A useful metaphor for understanding this in JavaScript is to think of it as deciding which building to enter. Just as you'd ask which building contains the desired office, you must identify the specific context to determine how this will behave.

## conclusion

`this` in JavaScript is a dynamic and powerful concept, designed to enable flexibility and reusability in your code. While it can be a source of confusion, a deeper understanding of how it works and some practical experience with different function invocations will empower you to harness its capabilities effectively. Shedding preconceived notions about `this` from other programming languages is the first step towards mastering it in the world of JavaScript.

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