loading...
Cover image for Destructuring the 'this' object in JavaScript

Destructuring the 'this' object in JavaScript

hunterheston profile image Hunter Heston ・3 min read

This is a short and simple trick for working with classes in JavaScript. Many programmers including myself that are comfortable with Object-Oriented Programming concepts are used to working with classes.

If you fall into this category then you probably like using classes in JavaScript like this:

class MyClass {
  constructor(){
    this.message = "hello there"
  }

  printMessage = () => {
    console.log(this.message) // valid prints "hello there"
  }
} 

Even though this is just syntax sugar on top of regular prototype-based inheritance. I find it easier to read.

One specific issue I have with the syntax of JavaScript classes is how members of a class, like message, are referenced in member functions.

In the example above we have to use this.message in printMessage even though printMessage is a member MyClass instance to reference message.

It would be a lot nicer to just do the following:

class MyClass {
  constructor(){
    this.message = "hello there"
  }

  printMessage = () => {
    console.log(message) // invalid prints undefined
  }
} 

It's a contrived example I know. But typing and reading this.message over and over and over again can be quite cumbersome if it is used in a function enough.

Then I realized that we can just use destructuring on the this object of member functions.

class MyClass {
  constructor(){
    this.message = "hello there"
  }

  printMessage = () => {
    const {message} = this
    console.log(message)
  }
}

Reference Types and Primitive Types

This approach has some drawbacks and gotchas when dealing with reference types and primitive types. It's good to be aware of them if you decide to do this.

Let's look at this class definition:

class MyClass {
  constructor(){
    this.message = "hello there" // primitive type
    this.items = [1, 2, 3] // reference type
  }

  printMessageMutable = () => {
    // copies the value of this.message into message
    let {message} = this

    console.log(this.message) // prints "hello there"
    console.log(message) // prints "hello there"

    // will not update this.message
    message = "there hello"

    console.log(this.message) // prints "hello there"
    console.log(message) // prints "there hello"
  }

  printMessageImutable = () => {
    const {message} = this
    console.log(message) // prints "hello there"
  }

  printItemsMutable = () => {
    // creates mutable a copy of the reference to this.items
    let {items} = this

    // both items and this.items reference the same array
    items.push(42)

    console.log(items) // prints [1, 2, 3, 42]
    console.log(this.items) // prints [1, 2, 3, 42]

    // now items and this.items reference different arrays
    items = [4, 5, 6]

    console.log(items) // prints [4, 5, 6]
    console.log(this.items) // prints [1, 2, 3, 42]
  }
} // MyClass

In printMessageMutable we create a local copy of this.message. That means that any changes to message in the function will not be reflected in this.message. If we needed to update this.message in printMessageMutable this might not be the best place to use destructuring on this.

In printMessageImutable we create a constant copy of this.message. So we are only ever planning on using the value of this.message and can't try to update it using the local variable message. This is a great case for using object destructuring on this.

In printItemsMutable we are updating an array. Arrays are reference types in JavaScript. When we call items.push(42) both items and this.items are referencing the same array. Because of this, both items and this.items will be updated. Later we set the local variable items to a new array [4, 5, 6]. Now when we print items and this.items we get different values. That's because this.items is still pointing to the old array initially set in the constructor.

Conclusion

That's it. I just think it's nice to be able to drop the this. especially if a member variable is a lot in one function. However, it won't be appropriate for all use cases.

Thanks for reading, let me know you disagree!

Discussion

pic
Editor guide
Collapse
blindfish3 profile image
Ben Calder

I've considered this myself. It can definitely make code feel more readable but there are a couple of drawbacks:

  • You can't easily distinguish class members from temporary variables declared in methods
  • IIRC you can't always assign a value to a destructured variable, so then have to remember to use this.myVar = newValue
Collapse
hunterheston profile image
Hunter Heston Author

I'm used to representing members with a _ at the end of their name, something like this.message_. I'm also used writing c++ which does not require the use of this to access members. So I have some biases that don't necessarily fit with the normal conventions or language JavaScript 🤷‍♂️.

And you're right there is some nuance for value types and reference types. I'm updating the post now to clarify them!