DEV Community

loading...
Cover image for Re-Realizing context

Re-Realizing context

Jay Gurav
Software Engineer, Front-end and Back-end developer with interest in building scalable, highly efficient, resilient and user-friendly systems.
Originally published at jaygurav.netlify.app ・4 min read

Often when getting started into programming most seasonal developers struggle their way into understanding this (especially in functional languages) and get into all sort of troubles that even further messes up things, however "this" becomes easy when they understand that this is all about the context of invoking a function rather than confusing yourself with from where it was declared(which is mostly the case in Object Oriented language).

It ain't what you don't know that gets you into trouble, Its what you know for sure that just ain't so -Mark Twain

If you find above terms difficult not to worry they are themselves a fully fledged topics to write an article or even a book to get into nitty-gritty details of this

In this post we'll try to learn about one of the implicit binding and how to explicitly set/bind context (this).

Implicit Binding

Implicit binding is when the context of invoking this is internally, intentionally and automatically bound to something else. These are considered as default standards/rules of how this behaves, for example

this with a getter or setter is one such example of implicit binding, consider another example below

function greet() {
  // this === userDetails //true
  console.log(`Hello ${this.fullName}!`) //Hello Jay Gurav!
}

const userDetails = {
  fullName: "Jay Gurav",
  greet,
}

userDetails.greet()
Enter fullscreen mode Exit fullscreen mode

When a function is called as a method of an object, its this is set to the object the method is called on. So in the above example greet method on object userDetails has this internally bounded to the object which invokes the function as its method using . operator, so the this === userDetails would result to true in greet function when called using userDetails.greet(). It to important to note that only the last property matters, so something like below

function greet() {
  // this === userDetails //false
  console.log(`Hello ${this.fullName}!`) //Hello John Doe!
}

const userDetails = {
  fullName: "Jay Gurav",
  greet,
}

const dummyUserDetails = {
  fullName: "John Doe",
  greet: userDetails.greet,
}

dummyUserDetails.greet()
Enter fullscreen mode Exit fullscreen mode

Here even if the greet property on dummyUserDetails points to userDetails.greet the this here in this case would be dummyUserDetails and not userDetails. So always remember in case of objects this refers to the object on which the method is invoked.

Explicit binding

so as the title says to re-realize context or to explicitly set this to some another context of our wish, JavaScript provides us with three useful methods .bind(), .call() and .apply() available to all functions through Function.prototype object to do so.

So we can achieve something that we intended to do above as follows

function greet() {
  console.log(this === userDetails) //true
  console.log(`Hello ${this.fullName}!`) //Hello Jay Gurav!
}

const userDetails = {
  fullName: "Jay Gurav",
  greet,
}

const dummyUserDetails = {
  fullName: "John Doe",
  greet: greet.bind(userDetails),
}

dummyUserDetails.greet()
Enter fullscreen mode Exit fullscreen mode

Here we explicitly say that we want to bind the method (not function) greet on dummyUserDetails with the context of userDetails.

call

The call method on Function.prototype object call a function with the provided context assigned to this, and the following arguments passed to the function as its arguments list.

Function.prototype.call ( thisArg, ...args )

The thisArg value is passed without modification as this value to the calling function. However it is important to note that when undefined or null is passed as thisArg then it is replaced with the global object and if the function is an arrow function then the thisArg will be ignored by the function.

for example

function log(ps) {
  console.log(`${ps} ${this.name}`)
}

const product = {
  name: "cheese",
}

const myPet = {
  name: "Bruno",
}

log.call(product, "Yummy") //Yummy cheese.
log.call(myPet, "🐢") // 🐢 Bruno
Enter fullscreen mode Exit fullscreen mode

apply

The apply method on Function.prototype object calls a function with the provided context assigned to this, and the array/array-like-object passed as the second argument that is passed to the calling function.

Function.prototype.apply ( thisArg, argArray )

Note that the syntax of .call() and .apply is almost identical , the fundamental difference is that .call() accepts an argument list while .apply accepts a single array of argument.

so in the above example we could be doing something like log.call(myPet, ["🐢", "🐰"])

bind

The .bind() method on Function.prototype object creates and returns a brand new function that when called has its this set to the provided context and the following arguments passed to the function as its arguments list.

Function.prototype.bind ( thisArg, ...args )

consider this below example


const user = {
  name: "John Doe",
  age: 42,
  getUser: function (){
    return {name: this.name, age: this.age};
  }
}

const user2 = {
  name: "Jane Doe",
  age: 25,
}

const globalContextUser = user.getUser;
globalContextUser() //{ name: undefined, age: undefined }


const boundedUser = globalContextUser.bind(user); //{ name: "John Doe", age: 42 }

const boundedUser = globalContextUser.bind(user2); //{ name: "Jane Doe", age: 25 }
Enter fullscreen mode Exit fullscreen mode

πŸ“š Further reading and resources

πŸ‘‰ This blogpost was originally publish at my personal blog site

Get in touch

Hey, have any suggestions, questions or concerns, You can find me Twitter, GitHub and LinkedIn. Help me make this better!, Feel free to drop me a message and I’ll get back to you soon Thanks!πŸŽ‰

Discussion (0)