
Javascript Fundamentals: ‘this’ keyword - bloomca
https://blog.bloomca.me/2018/08/24/javascript-fundamentals-this.html
======
stupidcar
I think the main reason "this" is considered confusing is that many
programmers learning JS have already spent years coding in Java or C#. They
expect it to work the same, then get frustrated when it doesn't. But the
reason it doesn't is due to the fact that functions in JS are first-class, and
don't intrinsically belong to a particular class and object instance they way
they do in those languages. As such, the context represented by "this" _has_
to be more fluid in JS.

That's not to say that the design of "this" is perfect. And it doesn't help
that JS' syntax was designed to resemble C-family languages that it differs
from significantly. But nor it is some entirely arbitrary and illogical flaw.
If you take the time, it's possible to gain an intuitive grasp of how
functions and "this" work in JS. But instead, learners are too often told that
"JS is quirky" by others who also have come to it from Java / C#, and never
move past memorizing to a list of these "quirks" to genuine proficiency. They
then grumble whenever they need to work on a JS codebase, because their
incomplete understanding makes it feel like they're standing on quicksand.

Soon such devs fall into a circular trap: They don't like coding in JS because
it feels weird and unintuitive. And they don't take the time to learn about it
more deeply because they don't like coding with it, and don't want to waste
time learning about something they dislike. But since they _have_ to code with
it, because it's part of their job, they just stumble along, hacking away and
getting frustrated when things don't work, then come onto Hacker News and blow
off steam about what a garbage language JS is.

~~~
curun1r
> due to the fact that functions in JS are first-class

I don't agree with that. There are plenty of languages with first-class
functions that don't have this kind of confusion. There's nothing about a
function being a value and being able to pass functions around that
necessitates the awkwardness of JavaScript's 'this'.

What does necessitate it is the lack of separation between functions and
objects. The fact that a function can also have its own properties is, to my
mind, a mistake in the design of the language. And it's a decision that those
other languages with first-class functions and no 'this' problem didn't make.
If objects and functions are distinct concepts in the language, it's trivial
to make 'this' be dead simple and allow programmers to have a simpler mental
model.

~~~
wruza
What model, exactly?

In perl/c[++]/#/java, you have to carry object around with a method (or use
functors), otherwise you’ll call it with wrong arguments. In python methods
are bound implicitly in a class, but explicitly in assignment (iirc). In Lua
you’re explicit with obj:mtd() instead of obj.fun(). In js, obj.mtd() is eqv.
to obj:mtd() in that sense, but you can bind() to anything.

Which model is preferable? ‘this’ is natural complexity, and under ‘use
strict’ it behaves just okay. It is programmers who don’t care to grasp it.

Saying that as a natural js hater.

~~~
blattimwind
> In python methods are bound implicitly in a class, but explicitly in
> assignment (iirc).

Oh, it's quite a bit more involved than that :)

I'm going to ignore Python 2 and the C API here.

In Python functions are descriptors. This means they have (mostly) a __get__
function which returns a closure passing self or cls or nothing (corresponding
to a regular method, a class method and a static method; @classmethod and
@staticmethod change the implementation of __get__, but of course they don't,
because everything is a special case in CPython; note that "regular methods"
are "regular functions" — all functions behave like methods if you treat them
like one). If you write

    
    
       class Class:
           def foo(self, x): print(x)
    

then foo is a regular old function. The magic is in the dot operator (quite
literally): instance.foo is a different operator from Class.foo, and
instance.foo is not Class.foo (one is a bound method the other is a plain
function). The former invokes the descriptor protocol to the effect of,
roughly, type(instance).__dict['foo']__.__get__(type(instance), instance),
which returns a closure calling the "real foo" (Class.foo) with instance as
the first argument. Similarly @classmethod has the effect of __get__ returning
a different closure, which passes type(instance) as the first argument
instead.

Python. It's complicated®.

~~~
wruza
Yeah, one most complex way to do it. I just tested my assertion for the sake
of truth and it seems correct:

    
    
      class Foo:
          def bar(self, x):
              print 'bar', x
      
      def f(x): print 'f', x
      
      foo = Foo()
      foo.baz = f
    
      print(foo.bar)  # <bound method Foo.bar of <__main__.Foo object>>
      print(foo.baz)  # <function f>
      foo.bar(5)      # bar 5
      foo.baz(6)      # f 6
    

Now who wants to say that 'js this' is quirky and complicated?

------
digitaLandscape
This would have been a good post in 2014 but it shouldn't be relevant to any
JavaScript developers in 2018.

Arrow functions don't have `this`, and `...` syntax is simpler and as
efficient as using `apply`. You only still need to use `function(){}` for
generators and custom constructors, which are rare.

~~~
sdegutis
Very relevant in 2018. React uses ES6 classes (inherit from React.Component)
so you're very often using `this` in React. In fact it's still a major source
of confusion still for many devs like me, especially when you create and pass
callback functions. I've been meaning to write a blog post on this so maybe
this is the motivation I needed.

~~~
digitaLandscape
Just use arrow functions for your callbacks!

This is trivial. Don't create a problem that doesn't exist.

~~~
sdegutis
This results in extra renders for an entire component chain, because comparing
two arrow functions will always be different whereas comparing two pre-defined
functions will always be the same. Hence React's recommendation to use 'bind'.

~~~
gear54rus
What I don't get is why the React guys don't just call the passed functions
with 'this' being the component, which seems like the most common use case. If
you need to override this, you could indeed use bind (it's not overridable
after that AFAIK so no logic is required from React side).

~~~
acemarke
Because it's really _not_ "the most common use case". Components often pass
down callbacks through multiple levels of children. A basic example would be a
Form component that renders something like:

    
    
        <Button onClick={this.onFirstNameChanged} />
    

The proper context for `this` is the Form, but the callback is used in the
context of the Button.

Besides, one of the main tenets of React is that "it's just JS", so normal JS
rules apply. Now, the old-style `React.createClass()` method _did_ actually
bind all "class methods" to the component instances so you didn't have to
worry about it, but that wasn't typical JS behavior. ES6 classes don't auto-
bind their methods, so neither does the `React.Component` base class.

The current recommended approach is to use the Stage 3 Class Properties
syntax, which allows defining class methods as arrow functions that are auto-
bound to the proper class/component instance. Very long-term, the React team
intends to introduce a "stateful functional component" API that doesn't have
to worry about class instances, but that's going to be a while. (There are
some assorted userland experiments with writing "`this`-less components" as
well.)

------
131hn
« this » is « the thing on the left side of the dot ».

(that’s not always true, nor a complete definition - but it helps me a lot in
understanding the whole thing)

------
anonytrary
This example is needlessly confusing:

    
    
      const a = {
        b: 42,
        c: function() {
          return this.b;
        }
      };
    
      (a.c || [])(); // 1
      (a.c)(); // 2
      (1, a.c)(); // 3
    

He's trying to illustrate that an expression returns a reference to the
(unbound) function. This line "(a.c || [])()" is bothering me -- why on earth
would this line exist in any program? Are we trying to throw a type error on
purpose? Maybe "(a.c || (()=>{}))()" would have been more appropriate.

I spent about 5 minutes wondering why each line returned 1, 2 and 3
respectively, when they should be returning the global value of b. If you are
going to be showing obscure, tricky examples of Javascript, the last thing you
want to do is have superfluous comments in your code. _Follow comment
conventions_ for the sake of your readers' sanity. This would have been much
less confusing:

    
    
      const a = {
        b: 42,
        c: function() {
          return this.b;
        }
      };
    
      (a.c || (() => {}))(); // undefined
      (a.c)(); // undefined
      (1, a.c)(); // undefined

~~~
wruza
>This line "(a.c || [])()" is bothering me -- why on earth would this line
exist in any program?

A variation of it is valid for method selection, such as:

    
    
      (cond ? a.foo : b.bar)();
    

or, for method fallback:

    
    
      (a.foo || a.fooDefault)();
    

Neither does work in js, though.

~~~
anonytrary
This is useful and works with functions that don't use `this` (like arrow
functions).

------
paol
It's really frustrating that the ECMA people didn't define class methods to
always be bound just like arrow functions. Like arrow functions, classes are a
new syntax so there are no backward compatibility concerns.

That said, having two different semantics based on which syntax you happened
to use to define a function is almost as big a programmer trap as the original
problem. It just goes to show there are no good solutions to fundamental
language design errors.

~~~
aphextron
>It's really frustrating that the ECMA people didn't define class methods to
always be bound just like arrow functions.

This is a huge annoyance because I never really know where to bind my events.
It always ends up just being an awkwardly placed "bindEvents" call somewhere
in the init function.

------
jaunkst
This is fine. It's not confusing. Don't expect JavaScript to be Java.

------
werkjohann
JavaScript Fundamentals: it’s “JavaScript”, not “Javascript” :P

------
lucisferre
I could summarize the `this` keyword in one sentence: Avoid it.

------
Kagerjay
honestly "this" is such a stupid construct in javascript, there's way too many
obscure and unuseful edge cases to memorize.

------
sonnyblarney
The answer to every question in this article, and any 'quiz' on how to use the
arbitrary and ridiculous 'this' in JS is simple: use TypeScript.

~~~
n_e
« this » works exactly the same in TypeScript.

~~~
sonnyblarney
I realize, but it's more of a reaction to the inanity of JS, moreover, since
I've started using JS, I've never once had any issue with 'this' \- the way
one constructs code often in TS is simply quite different i.e. it's really not
just 'JS with typing' even if you want it to be just that, usually it's not.

~~~
sdegutis
My TS code looks almost identical to my JS code, but with types. Some JS
patterns are a little harder to express in TS, but only because they're
inherently a little more difficult to implement correctly. Things like higher-
order functions (and HOCs).

~~~
sonnyblarney
My TS code looks like Java or Swift. If it's more than a quick function on a
web page, I'll never write JS again.

~~~
trgv
If your .ts code differs massively from your .js code you have no one to blame
but yourself.

~~~
sonnyblarney
Blame? Not only is this how I want it, I think it's the obvious and natural
progression of Typescript code.

Typescript is not just 'typing' for JS, it encourages much cleaner
abstraction, modularization etc. than JS.

My first Typescript exposure was a re-write of several thousand lines of JS
code. After a little bit of 'adding typing' I realized there were much better
ways to organize information. The result was powerful - what was once a near
hairball of code, was now magnificently cleaner, easier to read, easier to re-
factor, easier to maintain.

That experience not only validates the existence of TS but strongly highlights
the limitations of JS which has always been a deeply problematic language,
full of pot holes and weirdness, and though it's fully understandable why the
language evolved how it did - that's no justification for it's ugly failings
which cause existential inefficiency in many situations.

Though 'adding typing to JS' might be a good start for using Typescript in any
given scenario - anyone only doing that is missing a bigger opportunity.

~~~
trgv
The reason your posts in this thread are so irritating is because TS has all
the same junk people hate about JS. You say things like JS is "deeply
problematic" and "full of pot holes and weirdness" but you don't address how
TS has fixed any of these problems. (Hint: it hasn't. Fixing the language is
totally beyond the scope of TS).

Typescript provides concrete benefits. Maybe it also provide some benefits
that aren't so concrete, like encouraging developers to think about how
they're representing data earlier rather than later. But it doesn't "fix the
language".

~~~
sonnyblarney
Not quite.

I agree TS definitely does not 'fix' JS, but in practice, it helps avoid many
issues.

Example: prototype inheritance.

Now you can argue that's a 'good feature' of JS, but I'd argue it's just
convoluted and difficult.

Since programming in TS ... I've basically never run into a prototype
inheritance concern, and maybe a little bit daring (or lazy) to say that I've
forgotten many of the details. You really don't have to know anything about
prototype inheritance when you're in TS land.

Modules. Technically, that's going to be 'fixed' in a near future version of
JS, but really, it's not. Modularization is cludge in JS with various
versions. In TS it's fairly clear. No issues there either.

Also, annotations.

Anyhow, yes, you're right technically, but TS does obfuscate a lot of the
ridiculousness to the point wherein I would say TS is borderline another layer
of abstraction, not just 'types'.

It's the only thing in tech I actually talk up when I get the chance.

------
expertentipp
Not knowing this 8 years ago in interviews was the same disqualifying as not
knowing nowadays what is the latest Angular version and why is it incompatible
with the previous version. The difference is that "this" is still relevant -
just hit F12, enter "this", and hit <ENTER>.

~~~
anonytrary
If you're getting asked about Angular in your interviews, its probably because
you applied to a front-end job where they explicitly told you that you will be
using Angular 8 hours a day.

~~~
expertentipp
Front-end is not synonymous with Angular at all.

~~~
anonytrary
That's a straw man. I was talking about this:

> front-end job where they explicitly told you that you will be using Angular
> 8 hours a day

