DEV Community

JavaScript Joel
JavaScript Joel

Posted on

Rethinking JavaScript: The complete elimination and eradication of JavaScript's this.

R.I.P. this 1995-2018

nothis demo

If this is so difficult to reason about, why don't we just stop using it? Seriously. Why. don't. we. just. stop. using. it.?

If you have read How I rediscovered my love for JavaScript after throwing 90% of it in the trash, then you won't be surprised when I say I am throwing this away. this is gone. goodbye. this won't be missed.

With functional JavaScript, you will almost never see this. I say almost never because even though your code doesn't contain this, you have little control over 3rd party libraries. Popular libraries like React, jQuery, eventemitter2 and many others will force this down your throat.

Here are some examples of how libraries force us to use this.

Forced this in React

// 😞 GROSS: this
class Counter extends React.Component {
  constructor() {
    super()
    this.increment = this.increment.bind(this)
  }

  increment() {
    this.setState(s => ({ count: s.count + 1 }))
  }

  render() {
    return (
      <div>
        <button onClick={() => this.increment}>{this.state.count}</button>
        <button onClick={this.increment.bind(this)}>{this.state.count}</button>
      </div>
    )
  })
}
Enter fullscreen mode Exit fullscreen mode

Forced this in jQuery

// 😞 GROSS: this
$('p').on('click', function() {
  console.log($(this).text())
})
Enter fullscreen mode Exit fullscreen mode

Forced this in eventemitter2

const events = new EventEmitter2({ wildcard: true })

// 😞 GROSS: this
events.on('button.*', function() {
  console.log('event:', this.event)
})

events.emit('button.click')
Enter fullscreen mode Exit fullscreen mode

this is everywhere!

So what's the problem?

One problem is this is not accessible if you use an arrow function. Sometimes I prefer to write an arrow function instead of a classic function. Okay, I always prefer to write arrow functions.

Another problem is this can be unintentionally reassigned. So your function might fail based on how others use it.

// WTF? these will produce different outputs
const say = cat => cat.speak() //=> "meow"
const say = ({ speak }) => speak() //=> Error: Cannot read property 'sound' of undefined

// WTF? these will produce different outputs
cat.speak() //=> "meow"

const speak = cat.speak
speak() //=> undefined
Enter fullscreen mode Exit fullscreen mode

So let's just get rid of this completely.

NO. THIS.

I created a simple function decorator that to get rid of this. More on function decorators here.

After creating nothis, I created a package so I can use it in all my projects.

So what would this look like you ask?

nothis this in React

import React from 'react'
import nothisAll from 'nothis/nothisAll'

// 🔥 LIT: no this in sight!
class Counter extends React.Component {
  state = { count: 0 }

  constructor() {
    super()
    nothisAll(this)
  }

  increment({ setState }) {
    setState(({ count }) => ({ count: count + 1 }))
  }

  render({ increment, state }) {
    return (
      <div>
        <button onClick={increment}>{state.count}</button>
      </div>
    )
  }
}
Enter fullscreen mode Exit fullscreen mode

nothis in jQuery

$('p').on('click', nothis(ctx => console.log($(ctx).text())))
Enter fullscreen mode Exit fullscreen mode

nothis in eventemitter2

const events = new EventEmitter2({ wildcard: true })

// 🔥 LIT: nothis + destructuring!
events.on('button.*', nothis(({ event }) => console.log('event', event)))

events.emit('button.click')
Enter fullscreen mode Exit fullscreen mode

But wait! There's more!

fixthis can fix some of your existing this rebinding problems!

import fixthis from 'nothis/fixthis'

const cat = {
  sound: 'meow',
  speak: function() {
    return this.sound
  }
}

// 😞 GROSS: this is unintentionally rebound
const speak = cat.speak;
speak() //=> Error: Cannot read property 'sound' of undefined

// 🔥 LIT: this stays this
const fixedCat = fixthis(cat)
const speak = fixedCat.speak;
speak() //=> "meow"
Enter fullscreen mode Exit fullscreen mode

But I need help...

Install it...

npm install -P nothis
Enter fullscreen mode Exit fullscreen mode

Add it to your libraries...

import nothis from 'nothis'
Enter fullscreen mode Exit fullscreen mode

Play with it...

... and report bugs, request features or contribute to the project here https://github.com/joelnet/nothis.

This is the latest addition to my Rethinking JavaScript series. If this made you curious, check out a few of my other articles in this series:

Hit me up on twitter with any questions @joelnet

Latest comments (170)

Collapse
 
dielaughing profile image
J. Adam Moore

Amazing. It's nice to see so much push back from all the people I would never hire or choose to work with, ever. The people giving you grief will likely never do anything great or influential in their entire lives. They will never forge new ground or lead anyone anywhere.

I'm very impressed by your thinking and your effort. Good work.

I'm already using it everywhere.

Collapse
 
joelnet profile image
JavaScript Joel

This was anticipated. I receive similar feedback on many of my other articles. This one was exceptionally harsh though ;)

"Programmers are as emotional and irrational as normal people. So when the solution finally arrives, most of us will reject it." -- Douglas Crockford

If you like this one, you may enjoy this one too How I rediscovered my love for JavaScript after throwing 90% of it in the trash.

Appreciate the kudos. Cheers!

Collapse
 
filipekiss profile image
Filipe Kiss • Edited

I see where you are coming from and proposing new alternatives is always great, but in this case I, personally, think that nothis or fixthis are not a good approach. Unless JavaScript is completely redesigned in the years to come to make this work as expected (and I seriously doubt it. named functions and arrow functions already treat this differently, so you just choose what to use) and prevent all those pesky "gotcha!" moments we have every once in a while, adding a new dependency and new layer of complexity won't make anyone a better developer. It's far more useful to understand how this works and how you can use bind, call and apply to circumvent those problems then to add a new dependencie that will only mask those problems. I'd rather have someone on my team come to me and say "hey, I don't know why this isn't binding correctly" than see fixthis(this) on the code. this is not broken. It doesn't need to be fixed. this is just a rebelious teenager which we, sometimes, can't comprehend. Understand how this works and you'll see that correctly binding it is a far superior choice in the longterm than adding a dependency.

Collapse
 
joelnet profile image
JavaScript Joel

Unless JavaScript is completely redesigned in the years to come to make this work as expected (and I seriously doubt it

I agree. Can't be redesigned. It needs to remain backward compatible, so this will always remain and function as it does.

What I am proposing is the same thing others have proposed when suggesting programming languages should eliminate NULL (now considered the Billion dollar mistake).

If we can write our software in a way that eliminates an entire class of bugs, why would we not choose to do so?

If NULL is considered the Billion dollar mistake, how much is this going to cost us?

Collapse
 
careuno profile image
Carlos Merchán

I like using "this"

Collapse
 
joelnet profile image
JavaScript Joel

Can't argue with that!

Collapse
 
ddonprogramming profile image
Decebal Dobrica

I love the use of core-decorators, in particular @autobind, but the removal of "this" does not make any sense for me if OO is not ditched as well, you are supposed to use the current object in it's implementation. FP is amazing and the tools of it are plenty to go for this day and age. Great post.

Collapse
 
joelnet profile image
JavaScript Joel

While I do advocate FP over OO (in JavaScript), I don't think this is a keystone of OO. There are plenty of ways to create an Object without using this.

const Thing = () => {
  const someMethod = () => {}
  const anotherMethod = () => {}

  return {
    someMethod,
    anotherMethod
  }
}

const x = new Thing()
x.someMethod()

Much like null, this also introduces an entire class of bugs.

If we can completely eliminate an entire class of bugs (NullReferenceException) with null, then why can't we do the same with this?

Collapse
 
ddonprogramming profile image
Decebal Dobrica

haha, true. As a polyglot I can confirm this approach is rather difficult to stick to.

Thread Thread
 
joelnet profile image
JavaScript Joel

I agree, it is difficult. I come from a C# background. Very OOP. And at some point, I also wanted JavaScript to be like C#. Classes, inheritance, overrides, overloads, blah blah.

Heck, a few years ago I wrote WorkflowJS which was a recreation of Windows Workflow Foundation 4 in JavaScript. LOL

But of course this came with problems most of which I outlined in How I rediscovered my love for JavaScript after throwing 90% of it in the trash.

Life became simpler after understanding FP. There's just so much unnecessary complexity that comes with OOP.

Collapse
 
alejandra_quetzalli profile image
Alejandra Quetzalli 🐾

Nice. My team liked this.. sending you an email =]

Collapse
 
rrackiewicz profile image
rrackiewicz • Edited

I think it is worth mentioning (perhaps in a companion article) eliminating "this" ala classless OOP and the revealing module pattern. Now you have Javascript sans "this" covered in its entirety.

Collapse
 
joelnet profile image
JavaScript Joel

The revealing module pattern is a great pattern. That is how I used to write my JavaScript classes before going fully functional.

And yes, that pattern is a really good way to still create an Object without the need for this.

It would actually be better to switch your class to use the revealing module pattern. And if you can't, there's always nothis :)

Cheers!

Collapse
 
briansotodo profile image
b

Interesting post indeed, but I don't have a problem with this

Collapse
 
joelnet profile image
JavaScript Joel

Nobody does. Until they do. You probably also don't have problems with NULL. But you are still destined to run into NullReferenceException as well as problems with this in the future.

 
joelnet profile image
JavaScript Joel

I do have my eye on ELM. I'm really attracted to their claim of no runtime exceptions.

Collapse
 
joelnet profile image
JavaScript Joel

lol it sounds like you might like a previous article I wrote “How I rediscovered my love for JavaScript after throwing 90% of it in the trash.” @joelnet hackernoon.com/how-i-rediscovered-...

Some comments may only be visible to logged-in visitors. Sign in to view all comments.