Hacker News new | past | comments | ask | show | jobs | submit login
‘This’ in JavaScript (zellwk.com)
170 points by zellwk on Aug 3, 2017 | hide | past | favorite | 97 comments



I don't like the explanation as a set of piecewise cases. this just refers to the current object context. this refers to the window at the top level because you're operating within `window`. There aren't "simple functions" and "object constructor functions", just functions, and the `new` keyword executes the function within the context of a new object. Event listeners are not a special case, it's just that the callback is executed from the context of the event -- which makes the difference between `function (e) { this }` and `e => this` much easier to understand.


I agree. The key to grokking this is the difference between a function's execution context and its lexical scope. If you don't explain that I don't think you've explained anything, and if you do then there's no need to think about separate cases like the article suggests.


If you think of javascript code like an english sentence, then the function is the verb, the context (this) is the subject and the scope (arguments, closed over variables) are the nouns.


s/nouns/objects/g (then the analogy works for me)


The unfortunate thing about the lengths people go to in order to try to "fix" `this` in event handlers is that it wouldn't be a problem if people just implemented the event listener interface correctly.

PSA Don't do this:

  proto.foo = function() {
    /* ... */
    var self = this;
    element.addEventListener("click", (function(event) {
      self.iHaveAnUnhealthyObsessionWithLambdas(event.target);
    }));
  }

  proto.iHaveAnUnhealthyObsessionWithLamdas = function(clicked) {
    /* do something with clicked element `clicked` */
  }
Do this instead:

  proto.foo = function() {
    /* ... */
    element.addEventListener("click", this);
  }

  proto.handleEvent = function(event) {
    /* do something with clicked element `event.target` */
  }


The problem is that the approach is different depending on context. You are making use of the assumption that the addEventListener function knows to call the "handleEvent" method of the object in the second argument, and give it a proper "this" object. That's a big assumption, which should be documented in the code.

The code using "self" doesn't have this problem; it's more self-contained so to speak (no pun intended).


> That's a big assumption

No, it's not. At all. It's part of the DOM Level 2 standard that specifies what `addEventListener` does and has been for going on 20 years.

https://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-...


Well I don't see it used in a lot of code, so in that respect I think it is obscure. Also, using the "self" approach allows one to lexically bind to the enclosing scope, which aligns nicely with how most functional programmers think.


> Well I don't see it used in a lot of code, so in that respect I think it is obscure.

You just moved the goalposts.


Agreed. Much prefer 'var self = this' style. Keep the model transparent for future maintenance.

One time I don't is in Jasmine, where they explicitly state that 'this' is shared across all 'beforeEach', 'it' and 'afterEach' calls. Which backs up your first point.


This is where I've landed with the JavaScript that I write. It keeps things relatively sane, at least better than the other approaches I've tried.


I thought self had gone out of favor due to its relatively new global purpose.


ok, call it something else. I meant to say, save away the current ref to 'this'.


But then if you're listening to multiple events, won't you have to try to disambiguate after the fact in handleEvent()?


That's the fun part, because here you can code your event handling to read something more like a state machine. Here's a quick example in bogus code, adding and removing event listeners as we go along (and without keeping reference to all the callback functions):

  class ElementDragger {
    constructor(elm) {
      elm.addEventListener('mousedown', this);
    }
    handleEvent(e) {
      const elm = e.target;
      switch(e.type) {
        case 'mousedown':
          elm.removeEventListener('mousedown', this).
          elm.addEventListener('mousemove', this);
          elm.addEventListener('mouseup', this);
          break;
        case 'mousemove':
          elm.style.left = e.clientX + 'px';
          break;
        case 'mouseup':
          elm.removeEventListener('mousedown', this);
          elm.removeEventListener('mouseup', this);
          elm.addEventListener('mousedown', this);
          break;
      }
    }
  }


That seems complicated, fragile, and bad for performance in a DOM. Event bindings are slow (or at least, used to be).

I don't know the purpose unbinding mousedown on mousedown - it only fires once per mousedown doesn't it?

Why not just stick with `elm.addEventListener('mousedown', this.handleMouseDown.bind(this))`? Where's the benefit other than making things more verbose?


Bind is less performant, because bind creates a new function with new context. At the framework level, bind is avoided at all costs. @wiredearp's approach is correct, because it only adds a single event listener, everything else is conditional.


If you think the above code has bad performance because "event bindings are slow" then your preferred form using `bind` is going to have all of that and more; it sidesteps nothing (except extremely fast switch case matching) and only adds overhead, both in runtime and memory costs.

> Where's the benefit other than making things more verbose?

To be clear what you're asking, are you saying that:

    elm.addEventListener('mousedown', this);
... is more verbose than:

    elm.addEventListener('mousedown', this.handleMouseDown.bind(this))


If you plan to remove that event listener later on, you would need to go full verbose.

  this._callback = this.handleMouseDown.bind(this);
  elm.addEventListener('mousedown', this._callback);
  elm.removeEventListener('mousedown', this._callback);
  this._callback = null;
Compared to:

  elm.addEventListener('mousedown', this);
  elm.removeEventListener('mousedown', this);
  
@richthegeek I removed that event listener in my example to illustrate how easy it is, there was otherwise no good reason for it.


No, that a switch statement is more verbose than a single function.

Switch statements in OOP are a sign that something is wrong.


Switch statements are fine for simple tasks like the one in the example, especially earlier in a project where you do not know what you need in the future. You can always switch it to the correct pattern in version 2 as that way its better understood what hidden needs the code in question has, thus making it easy to pick a good OOP patern to replace it.

Then again, my jugment might be colored by working on codebases that are >21 years old.


It's however easy to read and that feels good if not right, since the common solutions to this imagined problem require substantial mental overhead as seen on https://hackernoon.com/rethinking-javascript-eliminate-the-s.... To promote "single functions" we could:

  handleEvent(e) {
    this[e.type](e, e.target);
  }
  mousemove(e, elm) {
    elm.style.left = e.clientX + 'px';
  }
... but then the discussion would almost certainly revolve around this questionable decision instead of the original topic.


> No, that a switch statement is more verbose than a single function.

You're vacillating and applying an inconsistent standard. The switch statement is used where multiple listeners are being attached, therefore it wouldn't work with your "single function". You'd need multiple functions.

Give any example passing functions directly as the callback to `addEventListener` to demonstrate your case, please. The equivalent version that simply implements DOMEventListener will be less verbose, be lighter on resources (both with memory use and with runtime), and have no need for any this-binding workaround weirdness.


Better yet element.addEventListener("click", self.handleClick.bind(this))


This is less performant, because bind creates a new function with new context. At the framework level, bind is avoided at all costs.


The overhead is not important, if you bind once. It becomes problem, if you do it too often, like in react render function.


Today I learned

In general, a good practice is to avoid anonymous functions for the listener else it is not possible to remove the event later.


I guess the function could still be anonymous, but you will at least need to keep it assigned to some variable or property name. If you make use of the `handleEvent` pattern that @carussell suggested, you can however avoid having to keep track of all these callback functions since the listener can always be removed by passing `this` a second time:

['click', 'focus', 'blur'].forEach(type => elm.removeEventListener(type, this));


> I don't like the explanation as a set of piecewise cases

We don't like it and it's redundant. One should learn the principles and return to this article to read it as a quiz and find out whether he got it or not.

Books like the excellent Javascript Allonge (free to read on leanpub) do a great job on explaining the context. If you need more hardcore stuff, "You don't know JS" will fit you better.

(Disclaimer: I'm not affiliated with any of the recommended books or their authors or leanpub or whatever.They just helped me a lot; and still do from time to time)


Agreed. It’s extremely poorly explained and quite misleading


> this just refers to the current object context

Exactly.

"If you can't explain it simply, you don't understand it well enough."

Albert Einstein


As an event listener "function(e) {this}" is not the same as "(e) => this". With the regular function "this" points to the element on which the event listener was attached to while with the arrow function "this" points to the context (i.e. "this") of its lexical environment.


That's what the OP was saying.


This refers the to the object left of the .


It's possible to write javascript without `this`, a style preferred e.g. by Douglas Crockford, explained in the video below.

https://www.youtube.com/watch?v=PSGEjv3Tqo0

I've also started to write my code mostly without `this`, `class` or `new`, not looking back.

Another related, highly recommend reading: https://medium.com/javascript-scene/the-two-pillars-of-javas...


> Another related, highly recommend reading:

Sorry, I don't follow your recommendation. Urgh, what a mess of a post.

All the (very valid) arguments that could be made for prototypical inheritance and functional programming style are drowned in a self-congratulatory fluff piece, full of marketing speech, substituting arguments by strawmen and namedropping, full of absolute claims and superlatives while demonstrating an rather pinhole view of computer languages, all the while not even giving a single practical example.


Me too (and later my organization)

It doesn't bring anything (except a bit more performances when you create tons of objects, a setup that rarely matters unless we're talking video games or some very niche applications that somehow can't work with a subset of your data) but only create more cognitive load (bind, call, =>, closures using this, where Am I? is it a method or a function? Is it already pre-bound?).

Functionalities that create extra cognitive load and doesn't give you anything shouldn't be used.


I absolutely agree.

As a related side rant, the `new` keyword, or equivalent, makes great sense in a manual-memory-management language like C, and it's an offense to sanity in a language like Java or Javascript. Factory functions should be the default everywhere. So any JS coding style that encourages `new` is a wildly bad fit to the rest of the language.

But it is important to admit the caveat, as you do, which is "when you create tons of objects".

Such situations are unequivocally in the minority (compared to line-of-business stuff where RAM and CPU more than sufficient to the task), but not the vanishing minority. So we do still need a good solution for those.

Rendering, simulation, sampling/signal-processing, parsing... the list goes on. This isn't truly systems-level code, but it's easy to generate a billion objects per second. So we do need patterns for doing this kind of programming.


> Factory functions should be the default everywhere.

Factory functions still need to create the object somehow to return it.


Absolutely. The guts of the factory function should usually involve an object literal. Or in some cases, Object.create.

(If we're talking about Java, then obviously you have to have `new` somewhere, which is just one of the many shortcomings of Java, although obviously not a fatal one.)


I do really like JavaScript's ability to compose objects like that, and I feel that people interested in it as a language should definitely learn how to program in that manner. However, I suspect that for most people JavaScript is a means to an end, and it's more useful in a number of senses to try to write JavaScript more like other class-based languages. I mean, I feel like things like TypeScript sort of miss the point of having a dynamically typed language, but there is a solid case to be made for features like strong typing, especially as size and contributors go up.



Parent poster here, thanks for pointing out this, I wasn't aware of the critics.


Crazy we're still talking about it.

I thoroughly understand `this` now after over a decade working in javascript, as well as thoroughly understanding `this` in Typescript which I use more now.

And I still make a mistake at least once a week. I know what I've done as soon as the code fails, but it's still so ridiculously easy to get into a context you didn't think you were.

Of all the mistakes made in javascript, `this` stands head and shoulders above everything else, with its arm round the prototypal class structure.


I almost never use "this" in my JS code. I find that when you use a functional style of development, you largely avoid the "this" pitfall.


That debate is long over.

If there's anything that a decade of functional style being "the next big thing" has taught us, functional style is not "the next big thing" and most programmers don't work well in it.


i strongly disagree with this. half of my posting history on here is evangelizing FRP in javascript (in my case via bacon.js).

it has dramatically reduced the amount of time i spend on these and many other sorts of problems. honestly it makes programming way more fun. maybe haskell is a bit extreme for most situations but the concepts are not.


While you may love functional, and I'm not saying you shouldn't, I think you're strongly disagreeing with the statement you think I'm saying, "functional is bad", rather than the actual statement I'm making which is "functional programming's had many chances to become a mainstream language and it never takes off (outside a few specialities)".

What evidence do you have that functional is taking off? Since this board was around, every couple of years there's been a flurry of 3 months when a chunk of the community bangs on about functional programming.

First it was Lisp, then Erlang, then Haskell, then F#, then Scheme. I might have got the order wrong.

And it never, ever, ever takes off because while 10% of programmers find functional truly brilliant, the other 90% can't work with it.

For context, in that time Ruby (via Rails), Python (via Django and use in Science), javascript server-side (Node.js), Go, Swift, Rust and now possibly Kotlin have all taken off. Plus various other techs like NoSQL.


Hm. I guess it depends on your definition of mainstream.

In the front end space, functional programming is taking off, I think. Could be wrong, but React + Redux + immutablejs is a popular and very functional approach to UI.

Elixir appears to be on the same trajectory as Rails in it's early days. Anectodally, I know a lot of Rails devs and shops that are now primarily Elixir.

Most mainstream languages have adopted functional styles and best practices. Higher order functions, a general acknowledgement of the benefits of immutability, etc.

Fp may not be mainstream, whatever that means, but it's more popular than I can ever remember it being.


This could depend on industry. In finance, functional is not the next big thing, but required. Needs context.


I may have misunderstood but do you mean that the prototypal class structure is also a mistake? If yes, in which way?


They ended up having to add classes (albeit as syntactic sugar).

Argue its advantages all you want, but I'd say that's representative of what a failure it's been. One of the last languages supporting prototype-based inheritance has basically given up on it.

Personally, I find it an awkward way to try and encapsulate code, generally, it has terrible performance to boot.


> One of the last languages supporting prototype-based inheritance...

Actually, all this time, both Python and Ruby use what we would call prototype-based inheritance.

https://www.reddit.com/r/node/comments/6oqiyn/is_es6_complet...

https://www.reddit.com/r/learnjavascript/comments/6c6use/how...


Was there ever any issues with prototype-based inheritance? I've always assumed that they added classes simply because of stubborn classical-based programmers who couldn't bother to learn JavaScript correctly.


That was my impression as well, especially since the class-ish syntactic sugar doesn’t improve performance either.


The numerous overloads of 'this' is one of the things that turned me off from JS. It's great that bloggers explain the usages and how it works in different scenarios, but really the better solution would be to not have the problem at all. I would trade overloading for verbosity. An editor can easily squash the latter, where as a human has to train for the former.


But there is no "overload" to `this` in JavaScript. It works the same way every time. It's an implicit parameter that is passed when you call the function. It can also be bound, just like all the explicit parameters. The six ways the author presents are just an incomplete list of "Ways you can create an execution context in JavaScript".

This may be confusing to people who expect `this` to have a certain functionality when coming from a different language, but `this` is very consistent in JavaScript. It's just not the kind of consistency people seem to expect.

There's a great way to avoid having the problem - don't use the implicit parameter! What exactly is it giving you, anyway? In almost all cases where people fail to use `this`, they essentially create a function to pass it explicitly. Why not just do that from the get go?


It may work the same way every time, but the way it works the same is different depending on context.


We can save time by listing the worthwhile features of JavaScript.

The OR operator (||) is nice syntactic sugar for returning the first non-null value. Just like SQL's COALESCE.

  let a = null
  let b = 123
  let result = a || b
result now equals 123.

Dang it. Now I can't remember the other thing I like about JavaScript.

Oh, I do like using the shebang preamble for running nodejs scripts from the bash command line.


    let a = 0
    let b = 123
    let result = a || b
sets result to 123, but 0 !== null.


EDIT: Rereading parent comment I realize you already know all the below, but maybe parent doesn't quite?? Original comment: That's kind of fundamental expectation within JavaScript, though: 0 is a falsy value. The || doesn't check for null, it coerces the first expression to a boolean, and if it is true, returns the first expression. If it is false, then it returns the second expression (if I recall correctly!). If 0 being falsy is a problem, then the || might not be the easiest/clearest way to do what you need. Since you'd end up needing to nest some separate check for zero on the first expression. In which case you could just handle all your logic in that check.


Simple(r) explanation; this is whatever is "left" of the function call (i.e - obj.method() => this === obj inside the called method), unless you've used .bind/call/apply which explicitly sets what this should be via an argument.

If you're doing a direct function call like myFunc() there is nothing "left" of the function call. But you might think of it as actually doing window.myFunc(), which again explains why this === window. In strict mode this behavior has been "fixed" so that this === undefined.

It's all about how a function is called.


What about setTimeout(obj.method, 1)?

(Spoilers: this == window, not what is left of the function call)


You are not calling the function, you are passing a reference to setTimeout that calls it directly, without the obj. before the call.

@getify explains it flawlessly, there are 4 modes: new, obj., bind/apply/call and default (window or error in strict mode)


As nhpatt notes you're not actually calling obj.method here, you're passing it as an argument, which will be called at a later time.

Looking at a possible implementation of setTimeout makes it clearer:

  function setTimeout(func, delay) {
    // wait for the delay to complete..
    func();
  }
Here you can see that when the function is called there's nothing to the left of it.


Yup, I get it. I was just pointing out that there are some edge cases to the "context is whatever is left of the function" rule-of-thumb.


Always name "this" and it will be easier to understand the context

  function Car() {
    var car = this;
  }

And if it already have a name, use that name:

  var foo = {
    bar: 1,
    say: function say() {
      console.log(foo.bar); // foo instead of "this"
    }
  };


"You don't know JS" series dedicated the whole book to "this" mystery : https://github.com/getify/You-Dont-Know-JS/blob/master/this%... Read this, 3 times maybe, and you will become the master of this.


Sometimes I wanna throw my hands up in the air and scream this is bullshit. But I can never figure out what this is


Somewhat off-topic, but I saw your newsletter sign up form partway through the article and thought to myself "I would like to see new articles from this blog". The issue is I don't want to sign up for yet another email newsletter, and would much rather subscribe to an RSS / Atom feed. Do you have any plans to add one?


If you can't get a positive reply, I suggest http://emails2rss.appspot.com/


There is an error in this article:

  const sayThis = _ => console.log(this)
  const boundFunc = sayThis.bind({hippy: 'hipster'})
  boundFunc()
Because you used an arrow function, `this` is not {hippy: 'hipster'}. You can’t rebind functions; once they’re bound, they’re bound.


> bind is a method that is present in every function

There is surely a world of unexplained knowledge in that line for someone that doesn't know JS well


In Jerboa, my JS-inspired scripting language, there's an alternative to the "function" keyword called "method". The only difference is that "method" functions expose `this' and "function" functions don't.

This removes the need for the `var self = this' hack and all the related weirdness.

An amusing consequence is that if you declare a simple closure as a method, its `this' pointer will be the stackframe of the lexical context where it is called.

    method test() { print(this.a); }
    var a = 5;
    test(); /* implicitly __stackframe__.test() */
(You should not actually do this, of course. It's fun though.)

The reason is straightforward: the `this' pointer is the object on whose lookup the closure was found...


So many new guides to `this` in the comments here. Not sure why this blog post exists either. I agree with most people that JS is confusing to people who are used to OOP programming in Python or Ruby, and that "this" is one of the most confusing parts. The best resource I've found is https://bonsaiden.github.io/JavaScript-Garden/#function.this . Free, online, concise, accurate.


Here's a fun one about `this` that you might not know. When running `eval`, `this` refers to the `this` in the calling context. Unless you alias the method. Then it refers to the global context.

    class Test {
        eval() {
            return eval('this');
        }

        aliasEval() {
            const e = eval;

            return e('this');
        }
    }

    const test = new Test();

    assert.equal(test.eval(), test);
    assert.equal(test.aliasEval(), global);


this is certainly tricky in JS :) I remember answering a question in a similar vein on SO a few years ago: https://stackoverflow.com/questions/6039265/50-50-false-50-5...

(I can't believe it was 6 years ago, time flies.)


`this` was the single-most challenging thing when I was learning JavaScript organically by myself. Now that I get it, it's pretty simple, but I also love that with arrow functions, I've had to worry about the unboundedness of `this` far less.


Somehow, after I understood this and prototypes in JavaScript it felt more OO than Java to me.


Coming from an 00P background, I never grasped this, and why it was made in such a way.

This made me avoid JavaScript as much as possible. Fortunately, I finally dived in, building my first js heavy webapp -- without a single this in it.


Not an incorrect article, but my opinion is that the focus should be informing people to not use this at all and more education on immutable data vs confusions on this


I agree, I only use this in classes in JS, outside of that usage I see it as an anti-pattern.


The one place I've used "this" outside the context of a class is when I built a monad as an object and decided to use set the "this" context to be the monad. So, for example "return/unit/pure" (or whatever you want to call it :-) ) is available as "this.return". In that way the functions passed to "bind" can be generic and still build the correct monad.

There are other ways to do it, but this struck me as the first time I had fiddled with "this" and felt that it was a reasonable idea.


`this` in Javascript is an anti-pattern and so is this article...

I wish classes didn't make into ES6, object-oriented programming is a waste of time.


What is the syntax used in the third example of "This in an object method"? I have never seen it, looks like an error to me.


It’s using object shorthand.

Rather than:

    {foo: foo, bar: function(){}}
You can just do:

    {foo, bar(){}}


It's new syntactic sugar for declaring object literals. Google "es6 object literals".


When you say "looks like an error", did you try running it? I assume you have a web browser handy.

All the examples in that section both look correct to me, and run as I'd expect in the JS environment I have closest-to-hand.

If you want help on the syntax, you should pose a more-specific question. Exactly what code looks like an error, and why do you think it's an error, and so on?


Sorry, I was on mobile and could not run it in the browser console. It was an honest question, but I already got the answer.


that is why sometimes we need to bind the this context in say like in jquery or react event handlers, they have different this every time! what a confusion isn't it?


And in non-strict mode this behaves differently as well.


    this = "madness!";
    if (this === "madness!") {
        this = "SPARTA!!!";
    }
(sorry)


thanks for sharing


I'm not sure I like this guide, because in several places it seems to imply the value of `this` is decided by where you define the function, which is only really true for arrow functions. For regular functions, unless you take control of `this`, it will be decided by the caller.

Best way I can think to describe `this`:

-----

1. Either you decide in advance exactly what you want `this` to be:

- Use `.bind()` on your function and fix the value of `this`

- Create an arrow function and get `this` from the lexical scope

- Alias `let self = this` in your desired scope, and only ever reference `self`

- Create a wrapper function which fixes the value of `this` (essentially, implement `.bind()`)

-----

2. Or `this` will be decided by your caller:

- Your caller will `.bind()` your function with an unknown value of `this`

- Your caller will call `.call()` or `.apply()` with an unknown value of `this`

- Your caller will attach your function to an unknown object, then call `unknownObject.yourFunction()`, setting `this` to `unknownObject`

- Your caller will call your function as a standalone function: `let newFunc = someObj.someFunc` - then call it, setting `this` to `global` or `window`

- Your caller will pass your function as a param to something like `setTimeout` and achieve the same effect, setting `this` to `global` or `window`

-----

Obviously, most of the time if you're going to use `this`, #1 is to be preferred, #2 is to be avoided, so it's imperative to decide in advance what you want `this` to be rather than allowing it to be decided by your caller.

(Unless that's absolutely what you want, and you have a good reason why -- like if you're writing a wrapper function which is agnostic to the value of `this` that's passed)


I always advise against calling your `this` alias `self`, because of https://developer.mozilla.org/en/docs/Web/API/Window/self - ie. if you missed out the `let self = this` line, then `self` will still work but it'll refer to `window`.


Surely this is going a little too far - there are tons of similarly innocuous names that will refer to globals if you forget to declare them. name, top, find, scroll, etc.

The real solution to the problem you're describing is strict mode and an editor that highlights undeclared variables!


You're right, of course, except that the entire design of `this` in Javascript is carefully tuned to make #2 easier and more frequent than #1. And we all gleefully exploit it when writing `[].slice.call(arguments)`.

I think it's an open question whether it was actually a horrifyingly bad design decision, or if it's rather that we're all too dumb to get it.




Consider applying for YC's W25 batch! Applications are open till Nov 12.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: