🚀How JavaScript Works (Part 8)? Binding
Tags: webdev, javascript, programming, tutorial
Published: 2023-10-26 10:24:02

In JavaScript, the `this` keyword is a powerful, yet sometimes perplexing, element. Understanding how it behaves is crucial for writing effective and maintainable code. We will discuss four essential ways to invoke a function and how each method affects the binding of `this`. We will explore these methods, understand their implications, and provide code examples.

### Table of Contents
[Function Invocation and the "this" Keyword](#function-invocation)
[Implicit Binding](#implicit-binding)
[Explicit Binding with .call and .apply](#explicit-binding)
[The Challenge of Losing this Binding](#losing-this-binding)
[Hard Binding](#hard-binding)
[Striking a Balance](#striking-a-balance)
[The new Keyword](#the-new-keyword)
[Default Binding](#default-binding)
[Binding Precedence](#binding-precedence)
[Conclusion](#conclusion)
[Sources](#sources)



## Function Invocation and the "this" Keyword <a name="function-invocation"></a>

When you invoke a function in JavaScript, the behavior of the `this` keyword depends on the context in which the function is called. Let's explore the four primary methods of function invocation.

## 1. Implicit Binding <a name="implicit-binding"></a>

Implicit binding is one of the most common and intuitive ways to handle the `this` keyword. It relies on the call site, which is the object used to invoke a function.

```javascript
const workshop = {
    topic: "JavaScript",
    ask: function () {
        console.log(`Welcome to the ${this.topic} workshop!`);
    },
};

workshop.ask(); // Outputs: Welcome to the JavaScript workshop!
```
In this example, the `this` keyword within the `ask` function refers to the `workshop` object. Implicit binding associates `this` with the object that triggers the function.

Implicit binding is a powerful mechanism for sharing behavior among different contexts. You can define a function once and use it across multiple objects, allowing flexibility and code reuse.

## 2. Explicit Binding with .call and .apply <a name="explicit-binding"></a>

Explicit binding provides control over the this context of a function using the .call and .apply methods. These methods take the desired this context as their first argument.

```javascript
const workshop1 = { topic: "JavaScript" };
const workshop2 = { topic: "React" };

function ask() {
    console.log(`Welcome to the ${this.topic} workshop!`);
}

ask.call(workshop1); // Outputs: Welcome to the JavaScript workshop!
ask.call(workshop2); // Outputs: Welcome to the React workshop!
```
With explicit binding, you explicitly specify the this context, allowing you to share functions across different objects but with precise control.

### Implicit Sharing of Behavior <a name="sharing-behaiviour"></a>

Implicit binding enables developers to share behavior across different contexts by referencing a single function. For example:

```javascript
const workshop1 = { topic: "JavaScript" };
const workshop2 = { topic: "React" };

function ask() {
    console.log(`Welcome to the ${this.topic} workshop!`);
}

workshop1.ask = ask;
workshop2.ask = ask;

workshop1.ask(); // Outputs: Welcome to the JavaScript workshop!
workshop2.ask(); // Outputs: Welcome to the React workshop!
```
In this scenario, you share a single `ask` function between `workshop1` and `workshop2`, invoking it with distinct contexts.
### The Challenge of Losing `this` Binding <a name="losing-this-binding"></a>

One challenge in JavaScript is losing the `this` binding when you pass a function around. Consider the following example:

```javascript
function delayedAsk() {
    setTimeout(function () {
        console.log(`Welcome to the ${this.topic} workshop!`);
    }, 1000);
}

const workshop = { topic: "JavaScript" };

delayedAsk(); // Outputs: Welcome to undefined workshop!
```
The `this` context inside the setTimeout callback function is not bound to the workshop object. Instead, it defaults to the global context, resulting in undefined.

### Introducing Hard Binding <a name="hard-binding"></a>

To address the issue of losing the `this` binding, developers often use a technique called "hard binding". This technique ensures that a function's `this` context remains fixed, no matter how it is called. The `.bind` method allows you to create a new function with a specific `this` context:

```javascript
function delayedAsk() {
    setTimeout(
        function () {
            console.log(`Welcome to the ${this.topic} workshop!`);
        }.bind(this), // Hard binding to the workshop object
        1000
    );
}

const workshop = { topic: "JavaScript" };

delayedAsk(); // Outputs: Welcome to the JavaScript workshop!
```
By using `.bind(this)`, you force the `this` context within the `setTimeout` callback to always reference the `workshop` object.

This approach provides predictability but sacrifices some of the flexibility inherent in JavaScript.

Hard binding is valuable when you need consistent behaviour across function invocations, but it should be used thoughtfully to balance predictability and flexibility.

### Striking a Balance <a name="striking-a-balance"></a>

The choice between flexible and predictable `this` binding is not accidental; it is intentional. Implicit binding offers flexibility, allowing you to share behavior among different contexts. However, there are scenarios where predictability is essential. The key is to strike a balance:

Use implicit or explicit binding when flexibility is required and different contexts are beneficial.
Utilize hard binding when you need predictability, ensuring that the `this` context remains fixed.
However, overusing hard binding may lead to a less maintainable and rigid codebase. Strive for a balance that aligns with your project's requirements, offering the right mix of flexibility and predictability for your specific use cases.

## 3. The `new` Keyword <a name="the-new-keyword"></a>

The `new` keyword is commonly misunderstood as a `class` instantiation operator. However, it's not inherently tied to `class`es. Its true role is to invoke a function with specific behaviours. When you use `new` with a function, it carries out four distinct tasks:

- **Creating a New Empty `Object`:** `new` instantly generates a new empty `object`. This is the object that the function will operate on.

- **Linking to Another Object:** The `new` keyword connects the newly created object to another object (to the prototype).
 
- **Invoking the Function:** The function specified after `new` is called. However, the function is executed with its `this` keyword bound to the new object, not the linked one. This is a crucial distinction.

- **Handling Return Values:** After executing the function, if it doesn't return its own object explicitly, the `new` keyword assumes that the function intends to return `this`.

### Deconstructing the new Keyword

The `new` keyword is deceptively simple, as it essentially hijacks the constructor function to accomplish these four critical tasks. The function's implementation is secondary to the actions carried out by new. Even if you use new with an entirely empty function, it still executes these four tasks. In essence, it's the new keyword that does the heavy lifting, not the function itself.

### Example of Using the `new` Keyword

Here's an illustrative example of how the `new` keyword works with a constructor function:

```javascript
function Workshop(topic) {
    this.topic = topic;
}

const jsWorkshop = new Workshop("JavaScript");

console.log(jsWorkshop.topic); // Outputs: JavaScript
```
In this example, the `new` keyword creates a new object, links it to `Workshop`, invokes the `Workshop` function with the `this` context set to the new object, and implicitly returns the new object.

## 4. Default Binding <a name="default-binding"></a>

The fourth way to bind `this` is the fallback, called default binding. If none of the other three methods apply, the `this` keyword can default to either the `global object` (in `non-strict mode`) or `undefined` (in `strict mode`).
```javascript
var teacher = "Kyle";
function ask(question) {
    console.log(this.teacher,question);
}
function askAgain(question) {
    "use strict";
    console.log(this.teacher,question);
}
ask("Whats the non-strict-mode default?");
// Kyle Whats the non-strict-mode default?

askAgain("Whats the strict-mode default?");
// TypeError
```
> In `strict mode`, the default behaviour is to leave `this` `undefined`, which can cause `TypeError` if you try to access properties on it.

## Binding Precedence

In JavaScript, understanding the binding precedence of the `this` keyword is essential to determine what it references when a function is invoked. Whether you're working with complex call sites or trying to unravel the intricacies of a particular code snippet, knowing the order of precedence for binding rules can be incredibly helpful. Consider this example:
```javascript
var workshop = {
    teacher: "kyle",
    ask: function ask(question) {
     console.log(this.teacher, question);
    },
};
new (workshop.ask.bind(workshop))("What does this do?");
// undefined What does this do?
```
A straightforward guide helps establish this order of precedence when determining the value of `this` in a function. Let's break down these rules step by step:

**1. New Keyword**
The first rule of precedence asks one simple question: Was the function called with the `new` keyword? If the answer is yes, the `this` keyword will point to the newly created object. This rule takes precedence over all others, making the `new` keyword a powerful way to explicitly define the context for a function.

**2. Call or Apply**
The second rule comes into play when the function is invoked using `call` or `apply`. It's important to note that `bind` is a sub-rule of this second rule, as it internally relies on `apply`. When `call` or `apply` is used, the `context` object specified as the first argument becomes the value of `this`. This rule allows you to directly control the context of a function.

**3. Context Object**
The third rule considers whether the function is called on a `context` object, like `workshop.ask` in the example. If the function is invoked within a specific object, the `this` keyword will reference that object. This rule provides a simple and intuitive way to manage `context` within object methods.

**4. Default Binding**
When none of the previous three rules apply, the fourth and final rule comes into effect. By default, the `this` keyword references the `global object`, except in `strict mode`. In `strict mode`, it defaults to `undefined`. This rule serves as a fallback when other binding rules don't match.


## Conclusion
Understanding the behaviour of the `this` keyword and the four ways to invoke functions in JavaScript is fundamental to writing efficient and maintainable code. By choosing the right method for your specific use case, you can harness the full power of JavaScript's dynamic and flexible nature while ensuring predictable behaviour where needed.

Ultimately, JavaScript offers a variety of tools for function invocation and this binding, allowing you to tailor your code to the demands of your projects. The key is to strike the right balance between predictability and flexibility, enabling you to create robust and versatile JavaScript applications.

But let's reset our minds back. The question that we set out to ask is, **if I have a `this`-aware function, how do I know what its `this` keyword points at?** And our strong temptation is, we want to assume that we can just answer that by looking at the function. What we've now seen is that **there's no way to look at the function to answer that question**. **You have to look at the call site**. **You have to look at how the function's being called**. Because every time it gets called, the how of the call controls what the `this` keyword will point at.

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