Recently I have an argument with a friend who absolutely hate the this
keyword in Javascript. He claims that since the language has an ambiguous binding of this
in different situations, e.g. const f = obj.g
will lose the binding to obj
, obj.g.call(obj2)
will call g
in the context of obj2
..., the this
keyword in Javascript is simply one of the worst thing in Javascript.
He also claims that Javascript would be a lot easier to code/maintain by avoiding the use of this
keyword. He advocates the following pattern for object creation:
function Car() {
const car = {};
car.position = 0;
car.move = () => car.position++;
return car;
}
new Car();
// or
Car();
I am, on the other hand, very comfortable with the this
keyword. I use it a lot and appreciate how bind
, call
and apply
works. However, I can't really find a legit argument against his pattern, because it really seem to be clearer for those who don't know much about Javascript?
What do you think? Give me some insights!
Top comments (39)
¯_(ツ)_/¯
here, I think you dropped this: \ (pun intended)
One thing about the pattern that your friend advocates is that it would be harder to use
instanceof
.You would have to do:
It's sneaky. I've wasted a decent amount of time and effort due to forgetting that
this
isn't bound in fat-arrow functions, or having a different binding than I expect at whatever point in the code. I still like usingthis
, but I'm not sure I could mount a bulletproof defense of it.I mostly agree with your friend, and I think ES6 has made it worse, because of how anonymous functions "break" the rules, which just confuses the hell out of junior devs:
I also kind of hate how everyone invents their own convention for re-assigning
this
to avoid context problems like so:Having said that, it's use is extremely prevalent, so I wouldn't make a crusade of this. It's a lot being that guy who's always complaining that everyone uses the phrase "begging the question" wrong.
Arrow functions were made specifically so that the second example wouldn't have to happen anymore. Consider the following.
Ignoring how terrible that code is, arrow functions allow you to have functions in functions while still maintaining the scope of your parent, yes but this behavior was very much requested and intended
I agree that this is a good thing....if you are senior enough at javascript to understand the nuances of the scope of
this
and you understand how arrow functions interact with this differently than "traditional" ones -- to many junior devs,() =>
is just shorthand forfunction()
. The fact that it isn't is mostly the fault ofthis
-- and is that extra "thing you need to know" really worth keepingthis
around?I've always had a love/hate relationship with magic variables (back when I first encounterd
$_
in perl my mind was a little blown).I can completely understand how avoiding them makes for more readable code.
Using
this
in any language is a hallmark of doing anything in OOP. Yes, JS has some things to watch out for, but the idea anyone would consider trying to drop usingthis
altogether instead of learning the time/place and make making great things so much easier escapes me.I think I agree with your friend. I don't hate the this keyword, I use it quite a lot myself. But I feel like the keyword this is prone to more errors than code trying to avoid it. Obviously, it all depends on the programmers writing the code.
To me, it seems safer to try to avoid using too much this. It also reads better I believe. When I'm reading code, I wouldn't want to ask myself every 2 lines: What is this this referring to?
This I think really depend on the design of your system.
In angular/vue/react, the framework seem to have developed around the keyword
this
without an issue. I wonder why they have made this choice ifthis
could be troublesome.I use React quite often. So, I use this a lot. I use arrow functions all the time, so every single this I use refers to the outer most instance, the component, every single time. Definitely a personal preference anyway :)
the only advantage that I could think of using
this
is the ability of reusing functions. Using the method suggested by my friend will create a new function instance for every object created. But if we use class, essentially we are using prototype, so all the methods will only be defined once. It makes a difference when we are creating the same instance many many times.It completely shatter the prototype which is bad IMO. Your friend's method not only create a new function each time, but prevent any ineritance.
Also, it's not possible to supercharge Car's prototype.
The "this" keyword can be tricky, but avoiding it leaves you in a land without OOP.
that was. mostly for the class base component i suppose
Yeah, but in react click handlers and other stuff most of the time need this, which leads to you either abusing class properties for arrow functions or having bind everywhere
While there are some historical gotcha's when using
this
in certain contexts, if you plan to use JavaScript in an OOP sort of way, I would in no way say that you should steer away from usingthis
. On top of that, constructors should not be defined with functions anymore. They should be in aclass
where usingnew
is forced and the meaning ofthis
is unambiguous.Secondly I would greatly avoid using
.bind
or.call
as much as you can. Some of the easiest ways to lose track ofthis
is using functions in this way.."since the language has an ambiguous binding of this in different situations"
Learn the situations, and use accordingly.
Refusing to learn how the tool you're using works will impede your work, or at minimum drastically reduces the quality of it.
I understand that JavaScript is "special" when compared to other languages, but working around it's eccentricities will only stop you from leveraging all that it has to offer.
In javascript, there are no absolutes. Your friend is wrong if they believe a single pattern will always be correct. That being said, some guiding principles can be helpful:
this
is valuable when you're working with a stateful object. You can ask yourself: is your object an instance of a "superclass", in that other instances of this object may exist and have different states? it's very likely thatthis
is the right tool for the job. In fact, as of es6 the module pattern your friend uses should most likely be refactored to aclass
.this
could be considered dangerous when working with raw objects. If you find yourself using.bind
a lot, or if you're callingthis
from inside a method of a non class-like object (e.g. you would never make more than one of the object/function), consider taking a more functional approach. With new Promises and arrow functions, functional javascript has become a lot more fun and readable.This argument IMO is generally a hold-over from when javascript didn't have classes, so you had the option of either constructing using the module pattern like your friend, or using a function constructor and appending methods to the function's prototype. Nowadays, we don't have to argue because we have the
class
keyword.Is
this
a minefield of possible mistakes in javascript? Absolutely not. It's part of the language, and gives us important OOP abilities. You should use it when appropriate!Have you seen Douglas Crockford's talk during Nordic.js 2014? There he said that he stopped using
Object.create
,new
,this
, etc. completely and instead uses the factory pattern for creating objects, similar to the pattern of your friend (around 25:00). After watching the video I tried it out and from then on I never missed these keywords. I rarely use the factory pattern nowadays, just if I really need it. Instead I try to code more functionalIt is only when
call
,bind
andapply
are used for the wrong reasons - or excessively - thatthis
starts to get unpredictable. In my opinionthis
is the exact thing that makes Javascript as powerful as it is.this
always points to the context of the call to the function within which the keyword is used.Arrow functions do what they do because the above statement does not apply to them. Instead, they inherit their
this
value from their declaration context.That's all there is to it. It is never arbitrary, nor should it be difficult to determine, where
this
points to. I think it's just a matter of awareness of the fact that you'll have to look for its value at the preceding call stack, instead of it's lexical scope.