DEV Community

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

JavaScript Joel on August 08, 2018

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 ...
Collapse
 
zeddotes profile image
zeddotes

I think finding a nothis or fixthis in your code is far more terrifying than dealing with this. Interesting read nonethless.

Collapse
 
belevatini profile image
Rocky Bronzino

I agree... Overcomplicating something just because you're not a fan of something is worse than learning to use it correctly. You can't assign a cat.speak function to a variable and expect it to be callable like a normal function. Methods implicitly get passed the object when called, so cat.speak() implicitly passes cat to speak. When you assign the speak function to a variable and call it directly it can't pass the cat anymore and will fail. The correct way to call from the assigned variable is speak.apply(cat) which will do same thing as cat.speak(). This is basic common sense when you understand how the language works.

Collapse
 
joelnet profile image
JavaScript Joel

For sure, experienced JavaScript programmers are aware of this. But a lot of JavaScript programmers out there are not. And A LOT of people are still get tripped up on this today.

for instance this code:

const logSpeak = animal => console.log(animal.speak())
const logSpeak = ({ speak }) => console.log(speak())

Would any reasonable person understand that if you use argument restructuring, the context to speak will be reassigned?

The best solution is to program without this. And when you program with a functional reactive style, you never have to use this, so you never have this problem.

Though you are not in control of 3rd party libraries and there are many 3rd party libraries which will force you to use this and the problems associated with it.

Thread Thread
 
nateous profile image
Nate

IMHO adding more libraries is worse for new developers. They won't know the difference between vanilla js functions and those of a library or framework.

Interesting concept but I'd prefer devs to learn the nuances of js.

Full disclosure: I'm a fan of vanilla js whenever possible.

Thread Thread
 
belevatini profile image
Rocky Bronzino

To me this is as obvious as x=y not being same thing as x="y" but I do understand the confusion to novice programmers, even "y" is confusing to some. I'm just suggesting that the language should not go out of it's way to cater to novice programmers.

Thread Thread
 
nateous profile image
Nate

Agreed, JavaScript is weird. But I love it! I also love C#. They're different languages with different works and benefits.

Thread Thread
 
joelnet profile image
JavaScript Joel

The best solution is to program without this at all. The next best solution is to fully understand this. When those fail, this just an alternative.

I agree that more people should favor vanilla JavaScript when possible.

Thread Thread
 
joelnet profile image
JavaScript Joel

To say nothis is for notice programmers, is to not understand all of the benefits that it can provide.

nothis will let you use argument destructuring. nothis will let you use arrow functions. nothis will name your context so you never have to write var self = this. nothis will let you write pure functions that output is computed solely on the input of other functions. nothis will let you use function composition.

Thread Thread
 
joelnet profile image
JavaScript Joel

This is why I love JavaScript. JavaScript can be what you imagine it to be. If you imagine a JavaScript without this you can have a JavaScript without this.

Thread Thread
 
zeddotes profile image
zeddotes

I'm happy you love JS, seriously. I think you have good intentions so as to provide the community with helpful utilities. I just wish that you would be more comfortable with this so you could see its use and benefits, especially leveraged with the fat arrow in particular.

If I could suggest something, take a look at apply and call Function methods; that should provide some clarity. Going beyond that, I'm curious to see what would happen if I did something like this:

noThisAll.call(this, this)
Thread Thread
 
joelnet profile image
JavaScript Joel

I think people have been trying to force OOP into JavaScript for a long time. I actually think classes were a mistake in JavaScript. But now that classes exist, people feel more and more comfortable with OO techniques.

JavaScript has Prototypal inheritance which is different than Class inheritance. But people coming from other languages start using JavaScript as if it were Class inheritance and that is where a lot of issues come from.

I feel like JavaScript is better suited for a functional reactive paradigm. Which doesn't require this at all.

The best solution is to code without this. Though sometimes you are stuck with this because of a 3rd library you are using.

If you look at the source code of nothis you will actually find that is exactly what it is doing behind the scenes. apply instead of call. github.com/joelnet/nothis/blob/mas...

So what you have suggested, is actually exactly how it works;)

Thread Thread
 
zeddotes profile image
zeddotes

There's no one best solution to every problem; what you're mentioning is subjective from case-to-case, dev-to-dev. You mention the use of functional reactive paradigm over OO, but then you're accessing this.event in your example; EventEmitter doesn't currently endorse that.

OO and Functional have their pros/cons and it's up to the infrastructure, architects, and developers to implement what works best as the solution to the problem, which is question to endless iterations of improvement over time.

We can sit here all day arguing about this, but I think it's clear where we both stand.

Ciao

Collapse
 
joelnet profile image
JavaScript Joel

The project is early and in need of polish. It is also in need of contributors like you to provide these suggestions.

Based on feedback I have created a helper function that abstracts away a lot of this boiler plate. How does this code look?

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>
    )
  }
}
Collapse
 
gmartigny profile image
Guillaume Martigny

Sorry, but I honestly laugh at

nothisAll(this)
Thread Thread
 
joelnet profile image
JavaScript Joel

The irony is not lost on me. I have opened an issue for this github.com/joelnet/nothis/issues/3

Thanks!

Thread Thread
 
joelnet profile image
JavaScript Joel

Your feedback has been invaluable in the creation of nothis-react. You can now create a Component like this:

import React from 'react'
import NoThis from 'nothis-react'

class Counter extends NoThis.Component {
  state = { count: 0 }

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

  render({ increment, state: { count } }) {
    return (
      <div>
        <button onClick={increment}>{count}</button>
      </div>
    )
  }
}

Thanks again!

Thread Thread
 
entrptaher profile image
Md Abu Taher • Edited

It would make more sense with a more expressive usage like this, noMore(this), instead of noThisAll(this), noThatAll(that) and so on.

noMore(fun), noMore(function)...lol...

Thread Thread
 
joelnet profile image
JavaScript Joel

I am the worst at naming. This was actually my first opened issue on github github.com/joelnet/nothis/issues/1 lol

noMore(this) this makes sense when using this, but doesn't if it was noMore(obj), which may happen.

Collapse
 
josetorreschang profile image
jose-torres-chang

In my short experiencie with JavaScript, had never faced an issue while working with this. What is so bad about it?

Collapse
 
joelnet profile image
JavaScript Joel

Don't worry, you will. Everyone does ;)

Check out this example:

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

cat.speak() //=> "meow"

const speak = cat.speak;
speak(); //=> undefined

You will also frequently run into problems with this when working with React.

// INVALID REACT CODE
class Counter extends React.Component {
  increment() {
    this.setState(s => ({ count: s.count + 1 }))
  }

  render() {
    return (
      <div>
        <button onClick={this.increment}>{this.state.count}</button>
      </div>
    )
  })
}

I have a few other examples on the github page github.com/joelnet/nothis

Collapse
 
cschliesser profile image
Charlie Schliesser • Edited

Those aren't problems to me, that's just not knowing how the language works. I don't mean to sound terse, but it's just never been an issue for me.

Thread Thread
 
chriscapaci profile image
Chris Capaci

Exactly.

Thread Thread
 
joelnet profile image
JavaScript Joel

Those aren't problems to me

There are others who do have these problems. Because one individual does not have a problem does not mean a problem does not exist. A quick search on stack overflow will show a lot of people have difficulties understanding this.

Thread Thread
 
maple3142 profile image
maple

I don't think understanding this is not that hard.
Besides, properly understand a language is a basic requirement to program in that language.

Thread Thread
 
zeddotes profile image
zeddotes

this is a complicated concept to grasp and I'm sure there is room for improvement in its design; however, it is not broken and especially not broken to the point where we need wrapper libraries to unbind contexts. Arrow functions were created to pass the parent context (this of the parent) into child scopes.

Thread Thread
 
lightest profile image
Nikita Agafonov

"Having difficulties" is part of learning process. That's how you grow.

Thread Thread
 
joelnet profile image
JavaScript Joel

I do not know a single developer that hasn't had to debug a this problem by writing console.log(this). It is not accepted that as a JavaScript developer, at some point you will have to debug `this.

it doesn't have to be

Thread Thread
 
lightest profile image
Nikita Agafonov

Duh! Ofcourse you will. Just like with any other code that you're not sure about YET. Then as you debug it to get an insight, to see what this points to you'll ask yourself - wait a second how did that happened? Does that mean... OOOH! And then the spoon bends, the matrix code appears and you get it. And that my friend is one of the big pleasures of working with your head.
It aint broken. Case closed.

Thread Thread
 
joelnet profile image
JavaScript Joel

Just FYI, saying case closed doesn't close the case.

You don't have to use the library. But please remember to think of me the next time you write console.log(this).

 
joelnet profile image
JavaScript Joel

Arrow functions solve ONE of the problems with this.

Here's a reference to this that you can't arrow function your way out of.

import { EventEmitter2 } from 'eventemitter2'
const events = new EventEmitter2({ wildcard: true })

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

events.emit('button.click')

nothis also does more than remove this. It lets you use arrow functions and also argument destructuring. Both of which are not options otherwise.

Thread Thread
 
zeddotes profile image
zeddotes

What is the purpose of trying to access this.event on the 5th line? If this.event was a value declared in the parent scope, the fat arrow would actually save you. The implication of using EventEmitter2 would be so that you can access arguments from the callback of the events.on method (ie. args passed inside the callback).

Thread Thread
 
joelnet profile image
JavaScript Joel

The code is correct. This is how their API is written. I need the this from the function inside the events.on method, not the parent scope.

It works the same way jQuery's this context works here:

$('p').on('click', function() {
  console.log($(this).text())
})

You can't write an arrow function for these.

Thread Thread
 
zeddotes profile image
zeddotes

Actually, jQuery calls the callback of the element's this passed into it via apply and call. Check it out: code.jquery.com/jquery-3.3.1.js

Thread Thread
 
benwick profile image
benwick

In jquerys events this is the same as event.delegateTarget: api.jquery.com/event.delegateTarget/

So you can use the arrow function to get access to the parent context and still access the element you have attached the listener to... Please don't stop using this just because you don't know how a lib works.

$('p').on('click', (evt) => {
  console.log($(evt.delegateTarget).text())
})
Enter fullscreen mode Exit fullscreen mode
Collapse
 
sampsonprojects profile image
Sampson Crowley

No, not "everyone does". People who don't understand the language do. There is only an issue with this when you aren't aware of how scope works

Thread Thread
 
zeddotes profile image
zeddotes

Precisely.

Thread Thread
 
joelnet profile image
JavaScript Joel

Which is A LOT of people! I'm not fabricating problems out of mid air. A very large number of people do have issues with this. You can tell by the number of articles written explaining the concept. The number of questions asked on stack overflow. Every JavaScript interview will include questions to see if you understand what this is.

It's a complicated subject for a lot of developers. I do not know a single developer that hasn't written console.log(this) at some point in their careers.

The problem with this cannot be simplified into "that's just how scope works".

This code is a good example of that. By simply using a destructured argument, the scope is unexpectedly changed.

const logSpeak = animal => console.log(animal.speak())
const logSpeak =({ speak }) => console.log(speak())

The reason why this happens is explained when you fully understand this. But to many developers they will stumble on this. Just search stack overflow.

Thread Thread
 
sampsonprojects profile image
Sampson Crowley • Edited

So because it's something that people have to learn, that means it's a problem? No.

Arrow functions are designed to preserve scope. That's what they exist for. There is nothing "surprising" about them. Just people that haven't actually learned the language

YOU DONT DESIGN CODE AROUND "DEVELOPERS" THAT DONT UNDERSTAND THE LANGUAGE.

Thread Thread
 
joelnet profile image
JavaScript Joel

If it's something that a majority of developers stumble on, then yes it's a problem.

Much in the same way NULL has been described as the Billion dollar mistake. NULL is a MUCH more simple concept to grasp than this. Yet now because we decide to use null, we have introduced an entire class of bugs into our application. All those lovely NullReferenceException bugs.

It doesn't matter how good of a programmer you are, YOU WILL run into NullReferenceException bugs.

Of course, there are ways to program without NULL. And this would eliminate that entire class of bug from your application. But we don't (myself included).

If we can program in a way that can completely eliminate entire classes of bugs, why would we choose not do to so?

Collapse
 
josetorreschang profile image
jose-torres-chang

I had face a bad time debugging working with React, when the error was just forgot to bind this, but nothing really bad. But with TypeScript works the same way? Working with TS never had any problem.

Thread Thread
 
joelnet profile image
JavaScript Joel

TypeScript is awesome. It definitely helps with this issues. The big problem of this in plain JavaScript is that you can never be sure what it really is. And your code may work differently based on how other people use it.

These two pieces of code could yield different results because this becomes unintentionally rebound to a different context.

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

const logSound1 = animal => console.log(animal.speak())
const logSound2 = ({ speak }) => console.log(speak())

logSound1(cat) //=> "meow"
logSound2(cat) //=> Cannot read property 'sound' of undefined.

So instead of worrying about what this is all the time, let's just get rid of this completely and we never have to guess again.

Thread Thread
 
josetorreschang profile image
jose-torres-chang

Oh, thanks for taking the time, to explain to me, to make it pretty clear. I used to think, problem was just about scope.

Thread Thread
 
joelnet profile image
JavaScript Joel

It is actually. it's just that your scope can change when you do not expect it to change.

Thread Thread
 
josetorreschang profile image
jose-torres-chang

So, is there any other options to avoid using this, that are native to JS? I know one of the quickest ways is var self = this, but not about anything that do not involves a variable. And one of your past comments, wake up my curiosity, if TypeScript is a superset of JS, what it does to work aroun with this issues?

Thread Thread
 
joelnet profile image
JavaScript Joel • Edited

TypeScript doesn't fix all this issues.

If you check out the REPL at typescriptlang.org/play/ and enter this code:

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

const speak = cat.speak
speak()

You'll find it still has problems with this.

My solution to avoiding this is to write JavaScript functionally and not to mix data and functions together into classes.

I would instead write the above like this:

const cat = {
  sound: 'meow',
}

const speak = ({ sound }) => sound
speak(cat) //=> "meow"
Thread Thread
 
josetorreschang profile image
jose-torres-chang

Although I can see what you are pointing, what is difficult, is to find the best way to avoid using this in a real application. Thanks a lot, this has been one of the most significant discussion this year.

Thread Thread
 
joelnet profile image
JavaScript Joel • Edited

That is because you have been taught to use this. With OOP, you are pretty much stuck with this (unless of course you use nothis). When you learn functional reactive programming, you'll find this to no longer be necessary and magically this just vanishes from your codebase.

Check out one of my projects here github.com/joelnet/bitcoin-all-tim.... This is a real world application. It follows what I preach from How I rediscovered my love for JavaScript after throwing 90% of it in the trash.

You won't find any references to this, var, let, if, switch, for, the function keyword or other staples of classic javascript programs.

Thread Thread
 
ilmtitan profile image
Jim Przybylinski • Edited

If you use typescript and modify the cat speak code to use an explicit if, you get a compile error.

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

const speak = cat.speak
speak() // <= Compile error: void is not assignable to { sound: string }.
Thread Thread
 
joelnet profile image
JavaScript Joel

It's pretty nice that this will happen at compile time and not runtime.

Collapse
 
kvsm profile image
Kevin Smith 🏴󠁧󠁢󠁳󠁣󠁴󠁿 • Edited

Not everyone does. I really think this is not an issue with this but an issue with people who insist on trying to write JS in an OOP style, perhaps because they came from an OOP background. The problem basically goes away when you use JS in a functional way, i.e. stop writing code which is called like animal.speak().

I don't think it's too hard to still use this in the contexts where it's required by libraries without shooting yourself in the foot, and at the same time eliminate it from your own code.

As mentioned by others, your invalid React code is really just an example of not understanding how the language works, and I don't think abstracting that away is a good solution.

Thread Thread
 
joelnet profile image
JavaScript Joel

Not everyone does. I really think this is not an issue with this but an issue with people who insist on trying to write JS in an OOP style, perhaps because they came from an OOP background. The problem basically goes away when you use JS in a functional way, i.e. stop writing code which is called like animal.speak().

^ THIS. This is the true solution. Write your software in a way that doesn't require the use of this ever.

But there are many people that come from OOP and want JavaScript to be OOP. I have been trying to convince people to write functional code for a while now. But I still receive the "HOW DARE YOU" responses. OOPers gonna OOP.

I just hope they think of me every time they write console.log(this) :)

Cheers!

Collapse
 
willydee profile image
Willy Schott

A total gun ban also may prevent you from shooting yourself in the foot.

Thread Thread
 
joelnet profile image
JavaScript Joel

The funny thing is, I think you were trying to use this argument to show how silly it is to remove this. But a lot of people are seriously proposing total gun bans for this exact reason, accidental shootings. It's almost as if your statement can be interpreted as in agreement with the article...

And banning guns will prevent a significant number of accidental gun deaths.

Collapse
 
qm3ster profile image
Mihail Malo

I get how it could trip up someone new to js, but that's what makes this a method and not just any old function.
Do you suggest replacing all classes with object factory functions?

class ligma {
  constructor(prefix) {
    this.prefix = prefix
    this.counter = 0
    this.statement = "ligma"
  }
  speak() {
    return this.prefix + " " + this.statement + " " + this.counter++
  }
}
const sugma = prefix => {
  let counter = 0
  const statement = "sugma"
  return {
    speak() {
      return prefix + " " + statement + " " + counter++
    },
  }
}

const PRE = "Very nice"

const l = new ligma(PRE)
const s = sugma(PRE)

console.log(l.speak())
console.log(l.speak())
console.log(s.speak())
console.log(s.speak())

Sure, s.speak is already bound.
Sure, memory and even code cache difference can be negligible when you don't instantiate even a thousand of these objects.

But what meaning does const {speak} = s have as a freestanding function?
Should we have it a static function and not a closure and take a state POJO instead: speak(s)?

Thread Thread
 
joelnet profile image
JavaScript Joel

I get how it could trip up someone new to js

I'll disagree with this statement. null is a much easier concept to understand when compared to this. Yet null is now currently considered to be the Billion dollar mistake or "The worst mistake in computer science".

We all understand null and we understand it very well. Yet we are forever doomed to run into random NullReferenceException errors. Even though we fully understand and comprehend null, you can never guarantee you will not run into those bugs.

So to assume problems with this are limited to Junior developers, I would say is an Optimism Bias.

Do you suggest replacing all classes with object factory functions?

In my perfect world, Objects and behavior would be disconnected. So you would never have a function and data combined together. We would have Categories and Morphisms (transformations) and immutability.

Then the world becomes simplified:

// data
const ligma = prefix => ({ prefix, counter: 0, statement: 'ligma' })
// behavior
const speak = ({ counter, ...rest }) =>
  ({ ...rest, counter: counter + 1 })

const l = ligma('Very nice')
console.log(speak(l))
Thread Thread
 
qm3ster profile image
Mihail Malo

And instead of encapsulating the state of a resource beyond our control in an object? IO monad?
How would you handle a WebSocket, a File handle, and such?

Thread Thread
 
qm3ster profile image
Mihail Malo

null is terrible. It shifts the job of knowing whether a function might return zero instead of one expected result to to the caller. And in JS we have two different nulls 👌
TypeScript with strictNullChecks begins to solve this, but there's the risk that something external claiming to return one concrete value might return a nullish value at runtime.

this is quite different. There's a couple of rules, and once you get them it works perfectly:

  1. In arrow functions, this isn't special it's just the binding from the enclosing scope. (undefined if that's nothing)
  2. In all other functions, including the object method shorthand on an object literal or a class, it's the enclosing object (undefined if that's nothing)
  3. Function.protptype.{bind|call|apply} first get the function, so it can't possibly be a method any more, so you get to provide your own this
  4. A binded function forever loses those arguments, just like you can't rebind a curried function's argument you can't rebind this. All it really does is
  Function.prototype.bind = function(thisArg, ...args) {
    return (...args2) => this.apply(thisArg, args.concat(args2))
  }

No one could possibly be overwhelmed by this.

There are many ways properties are different from scope bindings.

For example, one would not expect

const a = {b:1}
a.b++
console.log(a)

To have the same result as

const a = {b:1}
let {b} = a
b++
console.log(a)

Or consider getters and other capabilities of Object.defineProperty()
Treating properties and bindings with the same expectations is a very novice thing in the context of JS.

On the other hand in this beginner period I can definitely appreciate, for example when using ES6 classes, that the methods would be forever bound to the newed object.
But once you see what that desugars to, one stops thinking that.

Thread Thread
 
joelnet profile image
JavaScript Joel

Probably a combination of event emitters and rxjs.

Check out a project of mine here where I use these techniques: github.com/joelnet/bitcoin-all-tim...

On this page you can see how I consume a WebSocket: github.com/joelnet/bitcoin-all-tim...

Thread Thread
 
joelnet profile image
JavaScript Joel

No one could possibly be overwhelmed by this.

I'm am perplexed by your ability to understand the complexities of null and at the same time miss the complexities of a more simple concept being this.

Thread Thread
 
qm3ster profile image
Mihail Malo

What's the advantage of pointfree propEq usage? Isn't that less immediately readable than x=>x.type==='match'?
I feel like passing key names around in strings is strictly worse and should be avoided whenever it can. So much so, that if you have to use index notation on a non-Array object because your name is truly dynamic, you should actually be using an ES6 Map instead.

I can see the top Event, message, has an excuse to use Observable to filter, but why is

  Observable.fromEvent(websocket, 'error')
    .subscribe(err => events.emit('gdax.ERROR', { exchange, err }))

not just

  websocket.on('error', err => events.emit('gdax.ERROR', { exchange, err }))

And such?

Most importantly, how do you clean up the observables when the websocket closes? It seems that the websocket object and the three observables will never be collected, and a new call to default would just make a new one in parallel.

Thread Thread
 
joelnet profile image
JavaScript Joel

What's the advantage of pointfree propEq usage?

I just got into the habbit of using it. I can go either way on this one. The advantage is when you use something like path(['one', 'two', 'three']) instead of obj && obj.one && obj.one.two && obj.one.two.three so prop and propEq are similar which is why I used them.

I can see the top Event, message, has an excuse to use Observable to filter, but why is

Just for consistency again. So all "subscriptions" have the same interface. Again, I could have gone either way.

Most importantly, how do you clean up the observables when the websocket closes?

If I remember correctly, I am handling this by exiting the app completely. I have another process that restarts it. I had some issues with closing / reopening some websockets and this app wasn't critical to stay open, so I hard exit.

Collapse
 
kimlongbtc profile image
KimLongUn

you already have autobind(this) as a utility in react to prevent losing this context on event handlers which solves this issue in an easy and intuitive way without the need to bind everything manually.

i think your project is a nice idea considering how creative you got about solving that personal problem you got with this but on the other hand i think this is way to confusing to introduce in productive code bases.

handling this in js is not quite intuitive in some cases but there are only 2 - 3 tricky parts you gotta learn and this should be no problem at all.

Thread Thread
 
joelnet profile image
JavaScript Joel

autobind is a good tool too. It was created to solve one the problems with this.

There are always easy ways to work with this. But you will always run into bugs with it. I am just trying to imagine a world where you never have bugs with this because this doesn't exist.

I have created a project to demonstrate how easy it can be to use React without this here: npmjs.com/package/nothis-react

Collapse
 
theodesp profile image
Theofanis Despoudis • Edited

It looks like you know what you are doing in the github.com/joelnet/nothis package. Why are you falling into the traps of this then?

Thread Thread
 
joelnet profile image
JavaScript Joel

I see developers stumble on this. It's part of every developer interview process. Do they understand this?

In a functional reactive program, you won't find any instance of this. But there are times when code is outside of your control. For example when you have to use a 3rd party library. So you can never be truly free.

But your question should not be ** Why are you falling into the traps of this then?** it should be ** Why do JavaScript developers fall into the traps of this?** To which you will have to google and search stack overflow to understand the complexity of why people don't fully understand this.

Collapse
 
chriscapaci profile image
Chris Capaci

It's mostly people incorrectly using the language when they say this has problems. It's pretty clearly defined as to what it does and where it's used. I use it all over my code with no issues at all.

Collapse
 
sampsonprojects profile image
Sampson Crowley

If you're having so much of an issue with this, you don't understand JavaScript. Your scope can't "change when you don't expect it to" as you claim in a comment thread. YOU just aren't paying attention. I've literally never once had an issue with proper bindings. I bind or apply when needed, or use an arrow function when I need to keep a scope

Collapse
 
joelnet profile image
JavaScript Joel

You seem to be suffering from the Dunning–Kruger effect. Just because you do not have a problem does not mean that others do not. A simple search on stack overflow or google will show this is a problem for many JavaScript developers.

Take a look at this code:

const logSpeak = animal => console.log(animal.speak())
//=> "meow"
const logSpeak = ({ speak }) => console.log(speak())
// undefined

By simply converting a function to use argument destructuring, you will cause the function to fail. It is not reasonable to assume this.

When you bind or apply or use arrow functions to use the parent scope, these are all things that had to be learned. Not everyone is at that level. Do you remember WHY you had to learn those things?

I do not know a single JavaScript developer that hasn't written console.log(this) to figure out WTF this is. And if you tell me you haven't, I'll call you a liar ;)

Cheers!

Collapse
 
sampsonprojects profile image
Sampson Crowley • Edited

this is not surprising that it changes during destruction if you actually understand the language.

Objects have their own this. Do you understand how destructuring works? It's absolutely reasonable to expect this to change

Thread Thread
 
joelnet profile image
JavaScript Joel

Absolutely! There are very valid reasons why this is happening. And it goes all the way back to the early versions of JavaScript, back in the Netscape days before classes were introduced. I remember this because I have been programming in JavaScript for over 20 years now.

The ability to bind a this to a function made total sense back then. But then people tried doing OOP in JavaScript. People would attempt to make classes with Inheritance. JavaScript wasn't designed for that.

Now we have an actual class, which I believe was a mistake to introduce to the language. Because when people see class, they do not understand how it works under the hood. Class is just syntactic sugar after all. They incorrectly assume it will function in a similar way to how a class works in other languages.

So sure, after you understand everything surrounding the concept of this, you will run into fewer bugs with it. But you are still going to run into bugs. I do not know a single developer that hasn't written console.log(this) to figure out what this is. "Oh it's window. Duh!"

NULL is commonly referred to as the Billion dollar mistake. How much do you think this is going to cost us?

Collapse
 
chriscapaci profile image
Chris Capaci

Ridiculous. this isn't that bad, surely not bad enough to make such a big of deal over it. So, don't write an arrow function if you need to use this. It's not a big deal. Sure, the language should make it available on that situation and it's poor design that they didn't, but it's not the end of the world to use a different function form when necessary.

Collapse
 
joelnet profile image
JavaScript Joel

For experienced programmers, this isn't much of an issue. We are aware of the problems and how to correctly use it.

Though for the rest of the JavaScript programmers that have yet to achieve that level of experience, they will surely stumble on this. (as stack overflow questions suggest).

Bug with this are guaranteed. I have not seen a single JavaScript developer that hasn't written console.log(this) just to try and figure out WHAT IS THIS.

The best solution is to not program with this at all. Use a functional reactive style.

The library is just days old and needs a lot of work for sure. But take a look at this code (nothis v1.2.1). I would say this is more readable because of nothis.

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>
    )
  }
}
Collapse
 
phlickey profile image
Phil

It's like any part of the language though. When I started hacking things together with jQuery, I'd occasionally copy paste a snippet with this included and I'd look at it for a few minutes and decide that I knew enough about what it did to do what I needed to do and made a mental note to file it away til later.

Then I had to do something asynchronous in jQuery and using $(this) inside setTimeout broke what had worked just previously, so I deepened my understanding even further.

And then I decided to learn some OOP and I got even better about reasoning about how the parameter this is assigned at call-time. Now I will only occasionally come across a weird case that I wasn't expecting. You might call this stockholm syndrome, that I've developed a forced affinity for this, but then again, we're literally writing poems for rocks that can think so what even is normal.

Collapse
 
joelnet profile image
JavaScript Joel

I think once we've gained an understanding of this we forgot the journey it took to get there. We hold onto it as a badge of achievement. It's an accomplishment because it was so difficult. And now that we finally get it, why are we getting rid of it? After I spent all that time to finally learn it? Even knowing full well that we will run into issues with it again in our future.

My journey with this has ended. I'm not gonna keep it around for nostalgia.

My article How I rediscovered my love for JavaScript after throwing 90% of it in the trash might explain more of how I feel and why I am eliminating this.

Collapse
 
phlickey profile image
Phil

Not only am I going to run into edge cases with this in the future, but I'm going to have all sorts of problems with virtually every keyword in the javascript language at some point.

But ultimately, i feel that once you get them, you get to a point where you can write cleaner, more legible code. Whether that's a tangible, quantifiable ability or an imaginary boyscout patch is another matter. As an example, before I knew you could do short circuit execution with the && operator, I found code that employed it weird and unwieldy. Now it's a valuable tool I have to explain my ideas more precisely.

I really liked the hackernoon article you've linked, and am inclined to agree more with some of the other points, (var is dead, RIP for) and if you're coming at it from a purely functional point of view, then I'd have to agree. But I still find plenty of ways to mess around with object oriented patterns every now and then, and this is a useful component when trying to reason about things in that way.

Thread Thread
 
joelnet profile image
JavaScript Joel

But ultimately, i feel that once you get them...

I would disagree with this and the example I will provide is NULL. NULL is one of the simplest concepts to understand. So simple it could be the very first lesson of someone learning to program for the first time.

Yet NULL is now considered a THE WORST MISTAKE OF COMPUTER SCIENCE and is now being called The Billion Dollar Mistake.

We all understand NULL and yet we will be plagued with NullReferenceException's riddled throughout our code.

Just because it is the status quo is not a valid reason to not change the status quo.

If we know the steps to take to eliminate entire classes of bugs, why are we so hesitant to take those steps?

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

Thread Thread
 
phlickey profile image
Phil

It's an interesting idea, and as you say above, one of the reasons JS is now so ubiquitous is that you can mould the language in whatever shape you like just through coding style. I will, probably keep using this in my react components, and when doing anything that feels like it should be more object oriented, but it's great to have alternatives, and as functional dialects become more widely adopted, and thus more legible, I'm sure it's usage will probably decline anyway.

While this is a really interesting thought exercise about how different language features affect the code we write I don't think it'll affect the code I write today.

Thread Thread
 
phlickey profile image
Phil

Also, practically burst out laughing reading that article about null:

If a property of an object doesn’t exist, JS returns a value to indicate the absence. The designers of JavaScript could have chosen this value to be null.
But instead they worried about cases where the property exists and is set to the value null. In a stroke of ungenius, JavaScript added undefined to distinguish a null property from a non-existent one.
But what if the property exists, and is set to the value undefined? Oddly, JavaScript stops here, and there is no uberundefined.

Thread Thread
 
joelnet profile image
JavaScript Joel

Haha ya seriously. if NULL wasn't bad enough, we got stuck with two of them! lol

Thread Thread
 
phlickey profile image
Phil

Two values that don't have values and one number that isn't a number. Again, we get paid to make arrangements of minerals dream about words we write. It's not related, but I feel like every conversation about coding should be had in this context.

Collapse
 
patroza profile image
Patrick Roza

Why instead of nothis dont you just use classes with arrow functions and ban function() and methods from your linter?

Collapse
 
joelnet profile image
JavaScript Joel

Sometimes you do not have control over the code you are using. An example of this would be 3rd party libraries.

I am forced to use this here because that is how the library is written.

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

this prevents me from writing code like this

events.on('button.*', () => console.log('event:', this.event))
//=> Error

or like this:

events.on('button.*', ({ event }) => console.log('event:', event))
//=> Error

But with nothis I can write my function like this:

events.on('button.*', nothis(({ event }) => console.log('event:', event)))
Collapse
 
patroza profile image
Patrick Roza

Alright, so nothis is great for serving as an adapter to third party code, but I think for own code using the es6 constructs like class, arrow function and e.g linter should suffice.
I would also prefer to use the nothis on the events object to fix all its methods at once.
Or imo better have another wrapping approach that hides the poor use of this in events.on, and the use of nothis, so that we dont have to repeat it, nor remember it each time we want to use events.on

Thread Thread
 
joelnet profile image
JavaScript Joel

Totally agree. Great suggestion. I just added a helper function to apply all functions at once.

What are your thoughts on this?

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

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>
    )
  }
}
Collapse
 
avilapedro profile image
Pedro Ávila

Great article! Completely agree with you.

Another library that bothers me by forcing the use of this is Mongoose, here's an example from its docs:

// define a schema
var animalSchema = new Schema({ name: String, type: String });

// assign a function to the "methods" object of our animalSchema
animalSchema.methods.findSimilarTypes = function(cb) {
  return this.model('Animal').find({ type: this.type }, cb);
};

There are also functions that you can't import and destructure, because of this.

I would also like to add that this is an implicit argument for your functions, hence it can decrease the readability and comprehension of your code. BTW, that's why I hate using HOCs in React, it is hard to see how things relate to each other.

I also noticed that in your bitcoin-all-time-high project you're using Moment.js, although it's a great library, I would like to recommend you a more functional friendly one: date-fns.

Thank you for writing!

Collapse
 
dance2die profile image
Sung M. Kim • Edited

BTW, that's why I hate using HOCs in React, it is hard to see how things relate to each other.

Couldn't agree more. Render props partially fixes the problem but still you could get into pyramid of doom (unless you use a library like react-composer 😞)

And wow, bitcoin-all-time-high looks like a fun project. 💪

Collapse
 
joelnet profile image
JavaScript Joel

Totally agree. Render props is a great way to increase the visibility of your implementation by exposing values. I always go to a render props before a HOC.

It is a fun project. I can't wait for it to start tweeting again. Just gotta hit those all time highs again. lol

Collapse
 
joelnet profile image
JavaScript Joel

moment has been my goto forever. So long in fact that I never bothered looking for another. date-fns looks very interesting. thanks for the recommendation, I'll check it out!

Collapse
 
kepta profile image
Kushan Joshi • Edited

While I admire your creative solution and how calmly you are defending this idea, there are certain problems that you or any good person must acknowledge to better fight the this battle. I know my comment might get lost in this heated debate, but the majority of the comments talking about

I am too smart! To me this is not a problem

are ignorant and pompous 🍿

  1. I feel the best solution to the this problem, which others have pointed as well, is not using it at all. Your most common reply for that is ({ speak }) => console.log(speak()). I do agree that destructuring could be a disaster when dealing with context-sensitive this. But, I do not think that this problem is worthy enough to be dealt with an abstraction like nothisAll. You can come up with such particular examples for other javascript problems as well. But in all of these cases, a practical and a wise solution is to just to be simply careful!

  2. You do not (generally) cover a problem by putting a blanket(abstraction) over it (which Javascript loves to do btw). You save the abstraction for complicated problems like DOM updates or routing. Abstractions never really are a pragmatic choice for bad choices Javascript creators made. The reason we have an arrow function is that there was a widespread this problem and everyone was inventing their own wheel to fix it.

  3. Your abstraction might work well for developers like you, who are well experienced with nitty-gritty details of this. A junior developer like me would find the nothisAll too magical! Which in short would make me nervous about the code I am trying to modify. If I encounter a this problem, I have fellow developers like you in my team to help me debug it, but I do not have fellow developers who have time to learn yet another abstraction after all the recent Javascript abstraction fatigue.

  4. Abstractions are great when used widely, for example, JSX or virtual DOM. The end of lifecycle for great abstraction generally is that they become first party API (jQuery) or the language syntax (fat arrow) offers a solution to the problem that they were trying to solve.

  5. this is very much a part of Javascript language and yes it is flawed! but simply ignoring it means you are ignoring a significant part of collective Javascript work done by fellow developers. Also, to be very honest, sometimes the most pragmatic solution to a problem is simply using this.

  6. Lot of us don't have the luxury to work with fancy ES2015+ syntax and the web is built on the promise of backward compatibility. I see codebases which are full of advanced usage of this, which looks like (src code)

export function osmNode() {
    if (!(this instanceof osmNode)) {
        return (new osmNode()).initialize(arguments);
    } else if (arguments.length) {
        this.initialize(arguments);
    }
}

Sure you can come up with ways to handle it in your abstraction, but no serious and widely used project would consider the novel abstraction because of the added complexity (which I like to call magic) over the already complex this.

I do not want to sound against your idea, I very much like your creative approach. But if you ask me whether I would let my kids use it? My answer would be no.

Collapse
 
joelnet profile image
JavaScript Joel

Very well thought out reply. And I agree with quite a lot of what you have written.

I prefer to code in a functional reactive style. This style naturally doesn't contain this. So it's rarely a problem. It's the style I preach and I find JavaScript is perfect for it. The problems above would never appear. The problems I have appear when I use 3rd party libraries that force me to use this. Now instead of writing small algebras, I have to revert to writing classic functions with return and this.

I also wanted to demonstrate many different waysnothis could be used. Though, as you have already pointed out, the best solution is to not use this at all!

For many people this isn't as easy. It takes time to wrap your head around a classless codebase. Pure functions and immutability.

This is the reason why I am asking the question Do we have to use this?

As you have correctly pointed out, nothing comes without a cost. A library has a cost (even small ones like left-pad). Any abstraction is a cost. Even design patterns have a cost. So if someone thinks anything comes without a cost, is to not full understand the thing. nothis included.

So you have to weigh the pros and cons of nothis when considering it for your project. This means that (like anything with a cost) is not for every project.

PROS:

  • Confusion around what is this is gone. (no more console.log(this))
  • Functions attached to the context are bound so code like <Button onClick={ctx.handler} /> will work.
  • The context argument can be destructured.
  • Nested this contexts no longer require var self = this.
  • You no longer have to learn why you need code like this.method = this.method.bind(this) in your constructor. or why this works {() => func()} but this doesn't {func}. Or why your code works here, but stops working in a setTimeout.

CONS:

  • nothis is an abstraction and therefore must be understood by all developers on the team.
  • overhead of an added library.
  • overhead of having to wrap functions in nothis.

For me, the pros outweigh the cons and can be used today in any project and even projects that do not have any transpiling like babel.

Your abstraction might work well for developers like you, who are well experienced with nitty-gritty details of this. A junior developer like me would find the nothisAll too magical!

I would disagree with this point though. I think teaching a new developer the ins and out and all the gotchas of this is far more complex than teaching them about nothis. It is an abstraction, but I don't see anything overly complex about receiving the sender as the first argument. It's follows a similar pattern to C# event handlers:

void button_OnClick(object sender, EventArgs e) { }

But if you ask me whether I would let my kids use it? My answer would be no.

It would be best to first teach your kids to not program with this. Because that is the true solution.

Collapse
 
ahmedmusallam profile image
Ahmed Musallam

This is very cool, and I understand the issue and your solution. However, my problem with it is readability. Isnt it a lot easier to reason about “function(){}” than to perform the ceremony of calling notThisAll? Or fixThis?

Your proposal is really cool, it would take some getting used to. But if your worry is the novice developer, if they see this for the first time, they still have to lookup and understand what it means. Also, most code examples online will not have your library.

I’d argue that the time spent debugging the annoying issues of “this” is time well spent! That way you learn the ins and outs of the language.

Collapse
 
joelnet profile image
JavaScript Joel • Edited

There are pro's and con's to everything in this world. So for me to say nothis doesn't come with a cost would be a lie. There is a cost for sure. And you have to weigh the pros and cons before considering it for a project. This should be true with every library, or even design pattern.

Have a look at this:

no this demo

Does that look complicated?

Collapse
 
dechamp profile image
DeChamp • Edited

Doing away with certain parts of a language and replacing it with an excessive amount of additional code for the sake of "it's too hard" doesn't make sense.

Just take the time to learn "this" and why it's important part of JavaScript.

This feels kind of like "I don't know how to open this door, so I'll build a latter on to the house that lets me get in the top window."

I will give you props on building an alternative solution.

Collapse
 
joelnet profile image
JavaScript Joel

I find your language of "excessive amounts of additional code" to be either an extreme exaggeration or a vast misunderstanding.

nothis is just a function. A SMALL function. The function takes 1 argument and returns 1 argument. It is one of the most simple of functions that you can call.

Overall in a project, nothis sets to REDUCE the amount of code written as well as COMPLETELY ELIMINATE an entire class of bugs!

And YES we should get rid of parts of the language that cause developers to run into bugs!

If there are two ways to write code, 1 probably doesn't have bugs and 1 definitely doesn't have bugs, why not choose the one that definitely doesn't have bugs?

NULL has been described as a Billion dollar mistake. this is far more complicated than the concept of NULL. How much will this cost us?

The solution of "Just learn it" is what we are currently doing. And it is NOT working!!!

A simple search of stack overflow or google will show there are MANY developers that get caught up on this.

How does the “this” keyword work?
I have noticed that there doesn't appear to be a clear explanation of what the this keyword is and how it is correctly (and incorrectly) used in JavaScript on the Stack Overflow site.

I have witnessed some very strange behaviour with it and have failed to understand why it has occurred.

How does this work and when should it be used?

There are many articles written to explain how this works. this is not an easy concept for a lot of developers. I think because many senior developers forget that.

And in cases of React, if we forget about the bugs that may come with this the nothis-react package will let us write much more compact code and even support argument destructuring!

import React from 'react'
import NoThis from 'nothis-react'

class Counter extends NoThis.Component {
  state = { count: 0 }

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

  render({ increment, state: { count } }) {
    return (
      <div>
        <button onClick={increment}>{count}</button>
      </div>
    )
  }
}

Now that is SIMPLE code!

You may never agree with me, but please think of me the next time you write console.log(this) to debug this ;)

Collapse
 
dechamp profile image
DeChamp

in regards to your statement....

"I find your language of "excessive amounts of additional code" to be either an extreme exaggeration or a vast misunderstanding.

nothis is just a function. A SMALL function. The function takes 1 argument and returns 1 argument. It is one of the most simple of functions that you can call."


this is NOT a simple small function. It's a function that calls other functions and also includes lodash.

I honestly like what you did, it's clever and in some scenario could be helpful but I think you need to avoid writing an article that could lead new devs in to thinking that "this" is a horrible thing to use.

I feel more people would have been on board if you presented it in a manor of "hey, 'this' can be a headache but I have an alternative solution you can try".

I know it has to be frustrating to have an opinion and feel like people are shutting you down.
I'm not trying to say "you're wrong, I'm right!". Simply stating that if you present your argument as a "you are all wrong, this is the right way" then you will definitely meet some resistance.

Your library should be in production and used by thousands of developers before you start making it's a "suggested way". There are far too many scenarios where you're library could cause more harm than good and you are not even aware of those yet. Sure you'll fix them as them come, but you haven't had enough run time for you to even know what they are.

Keep up the good work, don't let our comments get ya down. Be open to what the community has to say. You rock. Have a good day.

Thread Thread
 
joelnet profile image
JavaScript Joel

this is NOT a simple small function. It's a function that calls other functions and also includes lodash.

Ahh very true, very true. Though it doesn't include the entire lodash library. Only the clone function is included. This was safer that writing my own clone function.

Your library should be in production and used by thousands of developers before you start making it's a "suggested way".

I'll agree with this too. But I'm stuck with a catch-22. Nobody will use it unless everyone is using it. So the only way to get everyone to use it is if everyone is using it.

Maybe a labeling of experimental? I'll think about this.

I know it has to be frustrating to have an opinion and feel like people are shutting you down.

Not at all. I understand the way comments or reviews work. If you are happy, you move on. If you disagree, straight to the comments! When people comment it gives me and opportunity to clear up any confusions that may exist!

The first thing I would teach is how to write software without the need for this. nothis should be a library when you are stuck with this due to circumstances out of your control.

Feedback much appreciated :)

Cheers!

Thread Thread
 
dechamp profile image
DeChamp

Thanks for taking the time to reply to everyone! I think people will use it and maybe even offer to add to it. I know half of the people I've talked to still struggle with this....

Thread Thread
 
joelnet profile image
JavaScript Joel

It's a longer conversation that goes beyond the scope of this article. Even though we've all read the Billion dollar mistake articles, we all continue to use NULL. Even I still do. I look at it now and grimace, but I still use it.

I know exactly how I would replace it in my code too. I'd use a Maybe with a Just and Nothing. But... in a lot of instances, I still don't. This is one of the next things I want to correct in my code. The elimination of any NullReferenceException.

There are a lot of things I feel this way about. Who knows. Maybe when I am done I won't be using anything except combinators. lol. (please no).

Until then all we can do is try to be better every day.

Cheers Amigo!

Collapse
 
dance2die profile image
Sung M. Kim • Edited

I've seen many articles that have a common theme that constraints foster creativity.

Constraining oneself from using this sounds like a challenge and might help people be more creative.

Collapse
 
joelnet profile image
JavaScript Joel

Yes! The best article I have read on this is Why Programmers Need Limits.

When you introduce the constraint of not using this, you'll find yourself programming functionally, which I love.

Functional Reactive Programming FTW!

Cheers!

Collapse
 
dance2die profile image
Sung M. Kim

Thanks for the link to the article, which looks intriguing as it targets developers.

Collapse
 
joelnet profile image
JavaScript Joel

When people come to JavaScript from other languages, they expect this to work like it does in other languages, but it doesn't.

In fact, this can be very tricky. And it can change on you when you do not expect it.

this gets us all. I do not know a single JavaScript developer that hasn't written console.log(this) to try and figure out WTF this is at the current moment.

nothis will get rid of that problem.

Collapse
 
___prototype profile image
Karakabakov

this is integral part of JavaScript and replacing it with a third-party library, in my opinion, is out of question.

I know it's not a big library but still it contributes to the bundle.

Cost of Javascript

Collapse
 
joelnet profile image
JavaScript Joel

The dependency and bundle cost is absolutely a cost that should be weighed when considering all packages. More people should think this way.

NULL is also an integral part of JavaScript. NULL has now been labeled the Billion dollar mistake. How much will THIS cost us?

Collapse
 
___prototype profile image
Karakabakov

All programming languages have quirks.. By following best practices, making regular code reviews, code refactoring and actually learning when/how to use THIS most of the problems can be avoided.

And what about those problems that survive all of the above?
Well in that case we have automated tests, unit tests and plain old manual tests.

Thread Thread
 
joelnet profile image
JavaScript Joel

"Because that is the way we have always done it" is not a valid excuse.

"We put additional checks in to catch these problems" is backwards. You are treating the symptoms, not the cause. Write your code in a way that those problems are unable to exist.

If NULL is the Billion dollar mistake and this is magnitudes more complex than NULL. How much is this costing us?

It's time to think outside the brackets { }.

nothis

 
joelnet profile image
JavaScript Joel

Oh sorry, I wasn't trying to suggest that this should work like it does in OOP languages. I was simply stating that when OOP developers come to JavaScript, they incorrectly believe this will work the same way it has for them in the past. And they will continue to program JavaScript they way they have their other OO languages.

I am not a fan of Classes in JavaScript to begin with.

Collapse
 
ethanstandel profile image
Ethan Standel

Do you realize that TypeScript fixes this much more elegantly by fronting all references to this with let _this = this;. I'm also much more pro-figuring-out-how-the-language-works rather than going down dependency he'll trying to patch it.

Collapse
 
joelnet profile image
JavaScript Joel

TypeScript fixes some problems with this. Arrow functions fix some problems with this. These solutions do not fix ALL problems with this.

Consider this code that cannot be solved with TypeScript or arrow functions:

import { EventEmitter2 } from 'eventemitter2'
const events = new EventEmitter2({ wildcard: true })

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

events.emit('button.click')
Collapse
 
ilmtitan profile image
Jim Przybylinski • Edited

You fix it with an explicitly typed this.

import { EventEmitter2 } from 'eventemitter2'
const events = new EventEmitter2({ wildcard: true })

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

events.emit('button.click')
Collapse
 
joelnet profile image
JavaScript Joel

The best solution is to program without using this at all. Which can be done in a functional reactive design.

The thing we are currently doing and have been doing since the dawn of time is teaching. But why do developers still continue to struggle with this?

Collapse
 
lightest profile image
Nikita Agafonov

This is nuts. Learning the properties of the language is part of the deal. Rules of using this are not hard and actually pretty straightforward. We'd be better off focusing on real world problems instead fixing imaginary "DnD rules".

Collapse
 
joelnet profile image
JavaScript Joel

If people actually understood the rules of this, Classes wouldn't have been added into JavaScript. This confuses many people as they expect them to work the same way they do in other OO languages like C#.

nothis is not cosmetic. This solves real world problems, like never having to debug this.

Every dev I know has written console.log(this) to debug this. What if you never had to... ever again...?

Collapse
 
alexantoniades profile image
Alexander Antoniades

IMHO this is exactly how we end up complicating things even more in JS. And then we blame JS for becoming so complicated with all those frameworks. This is how the language works and there are solutions to these "problems".

Collapse
 
joelnet profile image
JavaScript Joel

There is a tradeoff. pros and cons that have to be weighted. For me, the cons of this in addition to the pros of nothis (arrow functions, destructuring, bound contexts) outweigh the cons of an additional library.

Collapse
 
alexantoniades profile image
Alexander Antoniades

Fair enough!

Collapse
 
andrejnaumovski profile image
Andrej Naumovski

Instead of trying to 'fix' issues that aren't issues - I suggest encouraging new developers to learn the fundamentals of the language they're going to use before working on anything bigger.

Collapse
 
joelnet profile image
JavaScript Joel

This IS what everyone is already doing, education. This is the current plan of attack. How has it worked so far?

I actually encourage developers to program in a functional reactive design. When you do, you will find this just vanishes from your code base completely. With pure functions these issues never arise.

But that is not the case with all codebases.

There are also other niceties that come with nothis. You get arrow functions and argument destructuring! It isn't solely about eliminating this.

Collapse
 
andrejnaumovski profile image
Andrej Naumovski

AFAIK, that's not really how people learn today. People are rushing to get into the current 'in' framework/library and start working, they forget the basics and the building blocks of the language on which the framework is built, which in turn prevents them from writing good, performant code and also causes new devs to introduce bugs which they can't fix because they don't know how or why a certain thing causes a bug (especially not knowing how the this binding works in JS). My point was, we need to stop abstracting everything to make it easier for new developers and actually take the time to explain how the language itself operates. That's how I did it, I went from procedural in C to OOP in C++ and Java to a mix of OOP/functional in JS. I do prefer functional as well but having a stateful class component in React and not using this seems counter-intuitive to me. Also, simply destructuring this.props or this.state at the beginning of render() removes the use of this onward. Need to pass a function as a prop and keep the binding to this? Arrow functions as class properties is a cleaner solution.

Thread Thread
 
joelnet profile image
JavaScript Joel

AFAIK, that's not really how people learn today. People are rushing to get into the current 'in' framework/library and start working, they forget the basics and the building blocks of the language on which the framework is built, which in turn prevents them from writing good, performant code and also causes new devs to introduce bugs which they can't fix because they don't know how or why a certain thing causes a bug (especially not knowing how the this binding works in JS).

Totally agree!

nothis is not an excuse to not learn the language.

NULL is considered the billion dollar mistake. It's an incredibly simple concept. Vastly more simple than this. Yet 100% of developers continue to run into null reference errors still as we speak.

How much do you think this costing us?

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
 
michaelzaporozhets profile image
Michael Zaporozhets

I'm an awful nostalgist for mega constructors and the circa 2010 var _this = this; but your points make sense in most contexts. I think value of this is clearer when describing code that is referecing an instance environment of something. Especially at an entry level, I think this helps to lubricate the learning process when working with constructors.

Collapse
 
joelnet profile image
JavaScript Joel

I prefer a functional reactive style that doesn't require constructors or this.

I think the concept of this works great in other languages like C#. Because you know what this is. Though in JavaScript, people expect this to behave the same way it does in those other languages and it just doesn't :(

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
 
joelnet profile image
JavaScript Joel

I subscribe to the theory of Why Programmers Need Limits. I also subscribe to a functional reactive paradigm.

It goes against what I have been taught in school, but I have started to question why about a lot of things in the language. I ask myself stupid questions like "What if X didn't exist". Questions like these have led me to better code.

We just bang out a for loop without thinking. It's what we've been taught, what we are familiar with, so it's easy. But it's only because of repetition that the for loop is easy. Compared to other methods of iteration it's actually incredibly complicated. But because those other methods are less familiar, they seem harder. But they aren't harder, you just don't have that muscle memory to fall back on.

So I clear my mind of these biases, open my mind, and think from new. Why do we need this?

The true solution is to not code with this AT ALL. EVER. But in a codebase out of our control, this isn't always possible.

After I rediscovered my love for JavaScript after throwing 90% of it in the trash, I started to really enjoy JavaScript.

All those weird issues that other developers hate JavaScript for all went away.

this was just next on the chopping block.

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
 
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-...

Collapse
 
hutber profile image
Jamie Hutber

Having gone through the comments there seems to be a lot of bashing, which you don't seem to comfortable with OP. It's never easy to take criticism. I'd just say thanks for trying to fix holes where you see them.

For what it's worth I think that actually this kind of package should be targeted to more senior Devs rather than junior. In as much that. If I didn't get caught out by this in the early days so very much then I never ever would have had to read abs learn about this. I do believe if people were to use this they would then have no idea what this is actually doing.

Then you'd just have to justify to seniors why use this, less writing might be the way to go.

Collapse
 
joelnet profile image
JavaScript Joel

These comments were expected ;) Don't worry, it in no way discourages me. Having written many similar articles:

This response was expected. People subconsciously put up barriers against change.

The first barrier is The Familiarity Heuristic, (oversimplified) says when given two options a person will prefer the familiar option.

Another barrier is the Mere-exposure effect, which states you'll develop a preference for something simply by having repeated exposure to it.

There's also the sense of accomplishment for having conquered this. this is a right of passage for JavaScript developers and if I went through the pain of this, then all new developers should also have to go through it. If I take this pain away, then the struggle and achievements of having mastered that concept also vanish.

You know... when I was your age we had to walk in the snow, up hill, both ways.

I don't believe nothis is for beginners or for experts. It's for all JavaScript developers. The tooling is just new and needs to be simplified further. And as a matter of fact I have a new React Component that I will release shortly that will do just that.

It will let you create a component like this:

class Counter extends NoThis.Component {
  state = { count: 0 }

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

  render({ increment, state: { count } }) {
    return (
      <div>
        <button onClick={increment}>{count}</button>
      </div>
    )
  }
}

Now that is simple :D

Cheers!

Collapse
 
luispa profile image
LuisPa

I believe Javascript is one of the most fragile programming language. As a js programmer I think this gave js the power to be dynamic, paying a price dealing whit “this” kind of things.

You can use a lot of tools like ESLint, Babel or Typescript to deal with “this” and use more friendly syntax and still generate pure JavaScript.

I identify your intentions with your post, and it’s great to understand other programmers insights.

Collapse
 
joelnet profile image
JavaScript Joel

My favorite thing and my least favorite thing about JavaScript are actually the same thing, it's flexibility to be what you want it to be. :)

Collapse
 
pedronasser profile image
Pedro Nasser

Although I agree with the no-this movement. I wouldn't use that library in my projects.
What I think would be a better idea is to implement it in a fork of React or even request that feature to be added to the React core project. Don't you agree?

Collapse
 
joelnet profile image
JavaScript Joel

The best solution is to write your code without the need for this. JavaScript does pretty well with a functional reactive paradigm.

But when we try to OOP our JavaScript, we'll always be debugging this at some point.

Collapse
 
jochemstoel profile image
Jochem Stoel

So I went ahead and removed this from the interpreter. Without this so far no unexpected side effects besides the whole concept of classes/prototypes no longer working and the super() method thinking it is not a function.

nothis

Collapse
 
joelnet profile image
JavaScript Joel

Haha that's hilarious!

That is actually the best solution. If you write your software in a way that never requires this. You will never have to debug this. Classes were a mistake to add to JavaScript.

Collapse
 
jochemstoel profile image
Jochem Stoel

I read your articles. It is trippy. I thought I had invented my own style but apparently others have too and it is called functional programming.
Up until now I just referred to it as context free programming and many developers I worked with argue that my code is unreadable. It was refreshing to say the least to read your post and discover it's a thing.

Thread Thread
 
joelnet profile image
JavaScript Joel

You might enjoy looking at one of my more recent code base github.com/joelnet/bitcoin-all-tim...

It's in a functional reactive style.

I'm curious about your style if you have code to share.

Collapse
 
inf3rno profile image
inf3rno

Well it is hard to follow what you want to achieve here. If you don't like "this" in javascript classes, then try a procedural or functional approach instead or choose a different languge e.g. java, which uses "this" rarely, if you insist to oop.

Collapse
 
joelnet profile image
JavaScript Joel

I'm not sure what is so hard to follow. I could boil it down to 3 words: Stop using this.

The articles I write all preach a functional reactive paradigm. You might enjoy How I rediscovered my love for JavaScript after throwing 90% of it in the trash.

While my code doesn't require the use of this, there are many 3rd party libraries which force you to use this.

Example:

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

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

events.emit('button.click')

or choose a different languge

The "If you don't like it go somewhere else approach" is not a solution.

Collapse
 
grantwparks profile image
Grant Parks

Taking the O out of OOP.

Collapse
 
dharmax profile image
avi

This is the problem with people who use frameworks like react. They don't learn the actual language or proper engineering, and they try to solve non-problems, and write articles about it.

Collapse
 
joelnet profile image
JavaScript Joel

Absolutely. The true solution is to not program with this at all.

Though we do use 3rd party libraries and a lot of those libraries out there will force us to use this. And anywhere this exists, developers will continue to have problems.

Collapse
 
kayis profile image
K

Funny thing is, React is one of the most "unmagical" frameworks out there. Only Cycle and Callbags go a step further.

Collapse
 
dharmax profile image
avi

It's not (just) about being "magical". Even if you like the concept of react (which I myself so not), why not use preact,for example It's much better engineered. Or Inferno? And if you like an even cleaner conceptcan- Riot? I'll tell you why - FUD and general mediocrity.

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.

Collapse
 
alejandra_quetzalli profile image
Alejandra Quetzalli 🐾

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

Collapse
 
lluismf profile image
Lluís Josep Martínez

Maybe you should a pure functional language, and your problems will go away. What will be next, get rid of loops? :-)

Collapse
 
joelnet profile image
JavaScript Joel

I have considered it. I am looking closely at ELM right now. a very interesting language.

I think JavaScript lends itself very well to a function reactive paradigm and the code I write is completely free of this. There are times when I import a 3rd party library that forces me to write this to use their api and that makes me sad.

btw, the for loop died a while back in my article Rethinking JavaScript: Death of the For Loop

Collapse
 
kayis profile image
K

I like how this gets passes as first argument, somehow that makes everything look a bit less magical.

But I didn't have any problems with this since the inception of arrow functions.

Collapse
 
joelnet profile image
JavaScript Joel

It was designed in a way to mimic event handlers in C#

void Button_Click(Object sender, EventArgs e) { }

You always get your sender as the first argument.

Even though this might not be an issue for a lot of developers. It would still be nice to be able to use arrow functions or argument destructuring! :)

Cheers!

Collapse
 
graphicdesignel profile image
Graphic Design Elite

This has to be a joke. Why add needless obfuscation, additional packages, confusion for a developer who doesn't know this specific package? This isn't broken, don't try and fix it.

Collapse
 
joelnet profile image
JavaScript Joel

Not a Joke. Many people have problems with this. My recommendation is to write your code so you do not require this at all. When you do not have this option, say with a 3rd party library, you can use nothis.

The "Package" is just a function. This is how code is shared in JavaScript world.

Collapse
 
graphicdesignel profile image
Graphic Design Elite

I didn't read through the other comments or reactions or I might have seen you are serious about this ( no pun ), sorry about that. Now looking I see many discussions above, but many people ( not all ) have come to the same conclusion I did. As i read through some of your actual responses your goal seems to be to tackle the task of teaching new developers how to deal with this, I dont see this as a successful completion of that goal. BTW call the added code whatever you want, Id call it a package because id surely use a package manager to grab it, otherwise i wouldnt bother using it.

Thread Thread
 
joelnet profile image
JavaScript Joel

I wouldn't say nothis is for teaching new developers. I actually wanted the ability to use pure function, to use arrow functions and to also use destructuring. Things I couldn't do with specific 3rd party libraries.

In code that I control 100%, I never write this. So this is never a problem that has to be solved. Well... until a 3rd party library forced me to do so.

And after I rediscovered my love for JavaScript after throwing 90% of it in the trash, my code became much more pleasurable. A lot of issues just disappeared.

Have a look at the code here: github.com/joelnet/bitcoin-all-tim... for an interesting read. It's a functional reactive style and doesn't have many "staples" that most applications have. no this, if, else, switch, var, let, for. Yet without these things, it's still a fully functional program.

 
joelnet profile image
JavaScript Joel

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

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
 
careuno profile image
Carlos Merchán

I like using "this"

Collapse
 
joelnet profile image
JavaScript Joel

Can't argue with that!