Javascript "this" binding with examples
Published: 2023-11-18 17:31:15

In this article, we will explore a runtime construct for JavaScript language called `this`. Compared to other languages, `this` behaves differently in JavaScript.

`this` keyword is a way to implicitly pass information of an _execution_ context to running _execution context_. If you are not sure what those terms mean, checkout out my other articles on [JavaScript Runtime](https://dev.to/odus_ex/javascript-runtime-and-code-lifecycle-8dn) and [JavaScript Scopes in-depth] (https://dev.to/odus_ex/advanced-javascript-mastering-scopes-and-related-concepts-5787).

It is a way to provide data implicitly between different scopes. 

The value of `this` depends on which object it is bound to in runtime. This binding is decided using a set of rules which are always decided on the function call-site, and not where function is declared.


Let us try to understand this with a very simple example:

```js
var me = {name: 'Writer'}
var you = {name: 'Reader'}

function printNameImplicit(){. // declaration site
	console.log(this.name)
}

printNameImplicit.call(me) // call-site 1
printNameImplicit.call(you) // call-site 2

```

Using `call` (more on it later), we **bind** `this` of `printNameImplicit` function (or of the scope created by the function) to any object we want. `this` value then becomes that object, and `name` is displayed accordingly.

In a way, we "imported" `me` and `you` object in `printNameImplicit` function's scope at runtime. 

Consider another example:

```js
var person = {name: 'Adam'}

function addAge(age){
 this.age = age;
}

function addAddress(address){
 this.place = address
}

addAge.call(person,20);
addAddress.call(person,'House 1, Street A')

console.log(person) //{ name: 'Adam', age: 20, place: 'House 1, Street A' }

```

Using `this` we can build up an object or modify its existing properties.

## Bindings

As we have seen above, `call` helps us bind `this` of a function to an object. It takes two parameters, the first being the object we need `this` to be bound to, the second is the argument required by the function whose `this` is being bound.

But what happens when we have multiple arguments for the function? We can pass subsequent parameters to `call` or
we can use a  very similar construct called `apply`. It is almost identical to `call` except that its second parameter is an array of values, representing multiple parameters of the function. If we modify our above example, by combining the functions:

```js
var person = {name: 'Adam'}

function addAddressAndAge(address, age){
 this.place = address
 this.age = age;
}

addAddressAndAge.apply(person, ['House 1, Street A', 20]);
// or addAddressAndAge.call(person, 'House 1, Street A', 20);

console.log(person) // { name: 'Adam', age: 20, place: 'House 1, Street A' }

```

As mentioned above `call` and `apply` are methods are a way to bind `this` to an object. By default, in non-strict mode, `this` is bound to the Global Object. In strict mode, `this` is bound to nothing and hence its value is `undefined`. This scenario is called as *default binding*.

```js
// non-strict mode
var a = 10;

function print(){
 console.log(this.a)
}

print() // 10
```
In the above example, `this` is bound to global scope. In strict mode, the result would be `undefined` instead.


### Implicit Binding

Implicit binding refers to `this` being bound to the object decorated on the call-site of the function. Consider the following example:

```js

function printName(){
  console.log(this.name)
}

const person = {
  name: 'Adam',
  printName: printName // implicit 'this' binding
}

person.printName();  // call site decorated with person object

```
When we modify object `person` to include printName inside it, the `this` of `printName` is implicitly bound to `person` if the function call is made using the `person` object.

Also note that only the closest object reference at call site is bound to `this`. For eg. if we do:

```js
function printName(){
  console.log(this.name)
}

const person = {
  name: 'Adam',
  printName: printName // implicit 'this' binding
}

const anotherPerson = {
  name: 'Eve',
  friend: person
}

anotherPerson.friend.printName(); // Adam

```
`friend`, i.e. `person` object will be bound to 'this' because it is closer to `printName` function call. 

Implicit bindings are lost when the call-site is undecorated. When implicit bindings are lost, they fallback to default binding.

```js
let name = 'Eve'

function printName(){
  console.log(this.name)
}

const person = {
  name: 'Adam',
  printName: printName // implicit 'this' binding
}

printName() // Eve, in non-strict mode, undefined in strict

```
Implicit bindings are also easily lost or re-bound in-case of assignments and callbacks.

```js
let name = 'Eve'

function printName(){
  console.log(this.name)
}

const person = {
  name: 'Adam',
  printName: printName
}

setTimeout(person.printName, 1000) // Eve, implicit binding lost

```
In other words it do not automatically forms a closure with the bound object. 


### Explicit Binding

Explicit binding, as the name suggests, is when we explicitly bind `this` to an object using constructs like `call`, `apply` or `bind` which we saw above. Unlike implicit binding, explicit binding constructs allow us to bind `this` without actually altering the object its bound to.


```js

function printName(){
  console.log(this.name)
}

const person = {
  name: 'Adam',
  age: '32'
}

printName.call(person); // Adam
printName.apply(person); // Adam

```

However, explicit binding does not cure the problem of losing our bindings and is fragile like implicit binding. To tackle this, we can perform a certain type of explicit binding called as *hard explicit binding* or just *hard binding*.

Hard binding wraps the call-site in a wrapper function, this makes sure that the scope in which a function's `this` is bound to the target object is not influenced by other scope.

```js

function printName(){
  console.log(this.name)
}

const person = {
  name: 'Adam',
  age: '32'
}

let hardBind = function (targetFunction, targetObject){
  targetFunction.call(targetObject)
}

setTimeout(() => hardBind(printName, person), 1000); // Adam

```

Here, we provide a wrapping function, so that our calling code (which is `setTimeout`) is forced to perform binding through that wrapper function only, making sure that whenever `printName` is called, its `this` is bound to the `personObject`. 

> The above example utilises _Closures_. If you do not understand how? Take a look at my article on closure: [JavaScript Closures with examples](https://dev.to/odus_ex/advanced-javascript-closures-4j2p)


The wrapper function is a common binding design pattern in JavaScrip and hence it is supplied as a default construct by the language in the name of `bind`. The `bind` returns a function which is called every-time we want to perform hard binding. 

```js

function printName(){
  console.log(this.name)
}

const person = {
  name: 'Adam',
  age: '32'
}

let hardBind = printName.bind(person)


setTimeout(() => hardBind(printName, person), 1000); // Adam

```

### `new` Binding

To understand `this` binding created by `new` operator, let us quickly refresh how `new` works.

Calling any function with `new` makes it a _constructor call_. A constructor calls returns a newly created object, unless the function being called returns its own object. 

The `new` operator binds the constructor function's `this` to this newly created object. 

```js

function Person(name, age){
  this.name = name;
  this.age = age;
}

let person = new Person('Adam', 32)

console.log(person) // Person { name: 'Adam', age: 32 }

```


## Arrow Functions 

`this` behaves differently from arrow functions. Unlike regular functions arrow functions do not have their own `this`, they form a closure with the `this` value of enclosing scope. This behaviour is sometimes useful when we do not want to shadow `this` value by creating our own for each function declared. 

```js
const obj = {
 // implicitly bound this  
 getThisGetter() {
    const getter = () => this;
    return getter;
  },
};


const fn = obj.getThisGetter();
console.log(fn() === obj); // true

const fn2 = obj.getThisGetter;
console.log(fn2()() === globalThis); // true in non-strict mode

```

`this` keyword in JavaScript plays a crucial role in determining the runtime binding of functions to objects. Unlike many other programming languages, `this` in JavaScript is dynamic and relies on the rules set during the function call-site, not the declaration. We explored how this can be explicitly bound using methods like call, apply, and bind, allowing for flexibility in managing the context in which functions are executed.

That's all for now. Until next time :)