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>
)
})
}
Forced this in jQuery
// 😞 GROSS: this
$('p').on('click', function() {
console.log($(this).text())
})
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')
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
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>
)
}
}
nothis in jQuery
$('p').on('click', nothis(ctx => console.log($(ctx).text())))
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')
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"
But I need help...
Install it...
npm install -P nothis
Add it to your libraries...
import nothis from 'nothis'
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:
- The if statement
- Death of the For Loop
- Replace break by going functional
- Eliminate the switch statement for better code
Hit me up on twitter with any questions @joelnet