Hacker News new | comments | show | ask | jobs | submit login
JavaScript: Clarifying The Keyword ‘this’ (henrycode.tumblr.com)
155 points by henryzhu 1790 days ago | hide | past | web | favorite | 72 comments



JavaScript has a number of nice features that do make sense, and if you understand how prototype chains work and such the complexity is worthwhile. "This" hasn't been one of those features for me.

I'm sure "this" makes sense in some context, but it forces me to think about the implementation of the language way too much without any benefit that I consider it a misfeature. So I don't use it.

Instead, I capture it with another variable in a prototype declaration:

   var that = this;
and refer to "that" everywhere in the prototype. Tends to be a lot simpler, and usually for event handlers I'll create a closure to avoid the issue entirely.


The fact that people are still writing clarifications for what something seemingly as simple as "this" actually means is just another argument for avoiding it entirely.

It's a huge source of sneaky, little errors and you wont find any JS object I've written without the first line being "var self = this;".

Basically "this" considered harmful.


The this keyword is confusing, it's good to see posts clarifying it. However, while the rule in the post will be right in many cases, this list is incomplete, even for some common usages of this, and might lull folks into a false sense of security:

1. "To the left of the dot" should be "to the left of the dot or bracket". E.g. o["foo"]() also passes o as this.

2. When you pass callbacks to built-ins like forEach, you can supply a thisArg, and if you don't, this will be bound to undefined (http://es5.github.com/#x15.4.4.18). (EDIT): Copying from cwmma below. Some callbacks, like setInterval and setTimeout, pass the global object rather than undefined. I suspect that DOM callbacks tend to use the global object and JS callbacks tend to use undefined, but that's not a blanket statement by any means.

3. This isn't purely JS, but when interacting with the DOM, this is also implicitly bound as a DOM element in event listeners. E.g. document.addEventListener('click', function() { console.log(this); }) will print the document object when you click on the page. This is quite relevant for any JS web development; if it makes it easier to conceptualize, imagine that the browser is calling a method with the DOM element to the left of the dot.

4. When the caller is strict mode code and the function is called without method-call syntax, undefined is passed as the this argument rather than the global object. You should always use strict mode to avoid accidentally handing the global object around (in strict mode, the "set to global" option is skipped in http://es5.github.com/#x10.4.3).

5. When using this at the toplevel (which is allowed), it is bound to the global object even in strict mode (http://es5.github.com/#x10.4.1.1).

And a few more esoteric ones:

6. In addition to call and apply, Function.prototype.bind() can also change the this parameter of a function, and violate the "left of the dot or bracket" rule (http://es5.github.com/#x15.3.4.5, see boundThis).

7. Inside a with() {} block (which you should never use, but we're trying to cover our bases here), this is bound to the object passed to with in the parentheses.

8. If a property is a getter or a setter, this is bound to the object to the left of the dot or bracket in the field access or assignment expression. This actually matches the rule in the post except for the fact that the "call time" is implicit; there are no () in the expression o.x, but it may call a getter function for x that passes o as this (http://es5.github.com/#x8.12.3).

(EDIT): One more doozy:

9. The dot and bracket operators implicitly convert primitives like numbers, strings, and booleans to objects, so it's not exactly what's to the left of the dot:

    Number.prototype.identity = function() { return this; }
    var x = 5
    var x2 = x.identity()
    typeof x2 === 'object' (true!)
    typeof x2 === 'number' (false!)
You can get the raw primitive if you make the function strict:

    Number.prototype.identity2 = function() { "use strict"; return this; }
    var x = 5;
    var maybe_five = x.identity2();
    typeof maybe_five === 'object' (false)
    typeof maybe_five === 'number' (true)

Please correct me if I've forgotten any...


Do any of you just avoid 'this' and write your Javascript like infix Scheme? You can define objects via JSON literals with function values, and the result seems both simpler to think about and more pleasant to read. (JS engines are tuned for thissy code instead, I think unfortunately, but you can fall back to using prototypes where it makes a difference.)


Yes indeed. I don't bother with 'this' at all, except when forced to by some interface. I don't even bother to define objects most of the time, just top level functions that can easily be called from a REPL. That simple subset of JS is powerful enough to go a long way — long enough that I'm unconvinced one needs anything else, unless one believes one does — and programming this way feels liberating, like riding a bicycle instead of pushing a car.


Are you able to write large applications like this without things getting too messy? I'm interested because I usually write JavaScript in the complete opposite way than you just described (strictly OOP, almost like I'm writing Java). This also means that I use 'this' quite a lot. Do you come from a functional programming background? What do you think about languages like clojurescript?


My main work is on a complex application, but we write it in Parenscript, a Lisp that compiles to Javascript (so yes, I do like languages like Clojurescript). That gives us escape hatches for controlling complexity that raw Javascript doesn't have. On the other hand, I do still write some raw Javascript and I prefer the same style in both. As far as I can tell from various measurements, the Lisp I write is about half the size of what I would write in JS. That's a win, but not so big that it can be the only strategy for limiting complexity.

Do I come from a functional programming background? Not really. I use closures and lambdas a lot, but my code is rather imperative. My obsession when building systems is to keep the code as simple and small as I reasonably can. Mutable state and even global state don't scare me as long as they help with that. What scares me are architectural assumptions about how one "should" structure complex software.

This sort of thing is a bazillion times more fun to discuss in person, whilst drinking beer, than it is in the contactless format in which we find ourselves. I'd try to convince you that you might do well to drop your OO assumptions, but I'm afraid of coming across as shrill. There isn't enough context here to make one's claims nuancedly enough, and then also "nuancedly" is not a word.


So far, my code tends to not use this at all. However, I've not written any applications that go beyond a few hundred lines of JS (interestingly, most of these programs would have been a few thousand lines of Java, if I even bothered to try at all. For UI, the libraries for JavaScript far outstrip anything available for Java!)

I do seem to be gravitating toward an organization scheme that centralizes my applications data into a single place such that all moving pieces of the application get a shot at dealing with inbound messages in an orderly fashion. This allows me to decompose the application into a very small number of function types, each of which has an explicit kind of responsibility.

The only time I've been tempted to go back to OOP has been to write (pure!) functions that seem to just beg for it - complex validation routines in particular. But using OOP for the app as a whole seems to not only be unnecessary, but positively undesirable.


This is why I avoid using "this". Placing this comment next to any of the 9 points causes the comment to mean something different. However, "this" could be useful in circumstances where that is the exact effect desired. Even in such cases, the relevant object can be made explicit as an argument.

Some time back, I had to explain to some people how to think about "traditional OO" in JS, which I tried to make systematic in a post [1]. (Not trying to self promote here, but most other presentations I've seen start with the prototype mechanism. "this" is not brought in until much later in the post.)

[1] http://sriku.org/blog/2012/04/12/classes-and-javascript/

[edit: fixed typo]


When using addEventListener, "this" is not necessarily bound to the DOM node. You can also pass an Object whith a "handleEvent" function as attribute, instead of directly passing a function.

You can read more about it for instance here: http://www.thecssninja.com/javascript/handleevent


For 6), a question, is .bind not just returning a new function, which will .call your provided one with the supplied "this"? That is, I think it's better to reason about it not as an exception to the usual semantics, but as something that can already be done by any coder in the language itself, using the usual semantics.

And thank you for 5), I didn't know it! :)


That's a reasonable way to think about bind().

On a pedantic note, due to complications with the new() operator, you can't actually implement .bind() in JS using only .call() and .apply() and get the prototype of the resulting objects right.


Would you clarify?


My comment was wrong, I was mis-remembering a bug that I'd seen before. You can get everything correct with the prototypes, but you cannot perfectly emulate it.

The only thing you can't do with JS emulation of .bind() is the ability to create a varargs constructor (https://bugs.webkit.org/show_bug.cgi?id=26382#c29). But at the same time, I don't totally grok that example yet. The relevant bit of the spec is http://es5.github.com/#x15.3.4.5.2, which describes the [[Construct]] behavior of .bind().


Point 7 is wrong.

Example:

a = {k: 10};

b = {k: 20};

var fn = function () {with (a) {this.k = 30;}};

fn.call(b);

console.log("a.k: ", a.k); // 10

console.log("b.k: ", b.k); // 30


Thanks, not sure what I was smoking there.

I can't seem to go back and edit, though...


Thanks for the thorough response! I didn't know about point 9: using strict mode to prevent prototype functions from converting primitive types to objects. That weird behavior has bitten me a couple of times.


Maybe "call" and "apply" belongs to that list?

    Function.prototype.call(this, argument0, argument1...)
    Function.prototype.apply(this, [arguments]) // array for args.
In a related note I created a variation of .bind that can have its bounded arguments an 'this' overwritten called mixbind, and also the methods .mixcall and .mixapply that can be used to overwrite arguments too (you can use NULL or undefined to left holes): https://gist.github.com/4303394


Yeah, those were mentioned in the post as exception 2, but maybe I should make a note.


Yeah it's mentioned in the article, sorry.


Maybe 10) You can't directly change the value of 'this'

    this = {};
    ReferenceError: Invalid left-hand side in assignment;
But of course you can make sure 'this' is something you expect it to be.

    if(!this.tagName){
        throw new Error("Only DOM objects can used as 'this'")
    }


The ability to wrangle `this` via fat arrows is one of the nicest things about CoffeeScript.


Please fix the indentation in the image. I was so confused by it until I noticed that "location" and "locate" are both properties of the person object. They should both be indented the same.


Sadly, one last, unfortunate inconsistency:

All event handlers have the value of 'this' bound to the object that emitted the event:

  var div = document.createElement('div');
  div.addEventListener('click', function(e) {
    console.log(this); // prints <div></div>
  });


That's not a JS inconsistency, that's just the browser calling yourhandler.call(yourobject, eventobject).

The underlying statement is that "event handler" is not a concept in JS, it's just a way of calling functions that some implementations use for some things (browsers for actions, node for I/O, etc...).

In fact, this didn't use to be true of old IE versions if I remember correctly, so it's not a matter of the language, but of what the browser does with your function during event firings.


You can get the current emitting object via e.currentTarget. Modifying 'this', while cute, just makes yet another thing that must be explained to people when you start talking about 'this'.


Unfortunate? Would you prefer to bind that yourself?


Would you prefer to bind that ['this'] yourself?

Certainly. Why should some state be teleported in magically while other state (the argument 'e' in the above example) has to ride the bus? Does it belong to a different class of Being? I don't think so. This forces me to spend precious brain cells remembering which things go in which buckets and what the rules are for accessing the buckets - i.e. busywork - rather than the problem at hand.

The easiest thing is to explicitly bind to just that state you need and ignore everything else. That's more or less what the argument "e" offers: it's a bag of all the goodies you might need while handling your event. Clearly, therefore, the DOM element emitting the event should be available through that same mechanism – and as you generally can get everything you need in that department through things like e.target, I find it simplest to remain blissfully ignorant of 'this'.


Agreed. Too bad we're stuck with Javascript.

EDIT: I meant that as a pronoun.


You could probably factor exceptions 1) and 3) into the rule too!

         foo();
1) What's left of a bare function call is nothing - except for a blank line that leads to the top indentation level. In other words, what's left of it is the global scope. So, 'this' refers to the implicit global object in your environment. (Alternatively, in strict mode, since nothing is to the left of the call, 'this' is bound to nothing.)

    new Foo();
3) To the left of this function call is the 'new' keyword. So, 'this' refers to the new object that was just created for the constructor.


Another exception, functions invoked with setInterval have 'this' be window, even if they're invoked in context with it's own 'this'. This got me the other day. https://developer.mozilla.org/en-US/docs/DOM/window.setInter...


A workaround that I like a little more: http://javascriptisawesome.blogspot.com/2011/11/setinterval-...


There are totally a bunch of workarounds, it's just another exception to this, I was using CoffeeScript so I was able to grossly simplify the MDN one https://github.com/calvinmetcalf/communist/blob/master/src/s...


One exception to the first exception: in a function with "use strict", "this" is never the global object.


I don't see the "exception to the exception", he is saying that if strict mode is on, "this" isn't the global object when saying foo(), it's undefined. And that's true.

It's possible to both have "use strict" and "this" be the global object (i.e. it's not true that it's never the global object), as simple as...

  "use strict"; // global scope
  var x = 1;

  (function() {
  "use strict";
  return this.x;
  }).call(this);


I am not sure that's quite what you want. Declaring x as a 'var' does not make it a property on the global object. Your example will return an 'undefined'.

I think this is what you want

  "use strict";
  this.x = 1;

  var ret = (function() {
    "use strict";
    return this.x;
  }).call(this);

  console.log(ret); //returns 1


I wrote "// global scope" beside the first statement to signify that that statement was run in global scope, same for the statement below it.

In global scope, the activation object is the global object, and so saying "var x = 1" will make the global object have a property x equal to 1.


Fair point, but I thought it was clear I was referring to the first "exception", that is, calling a function like "foo()". Your example, while interesting, doesn't apply.

I'll be more clear:

> When there is no ‘.’ the keyword ‘this’ is bound to the global object window.

False if the function has "use strict".


var self = this; why do people bother with 'this'?


because you sometimes want to refer to the original 'this' reference inside an inner nested function scope, where the actual 'this' keyword is rebound to the context of the inner function.

    function outer() {
        var outer_this = this;
        var inner = function() {
            var inner_this = this;
            // ...
        }
    }
http://stackoverflow.com/questions/4886632/what-does-var-tha... has some more details.


Of course there are exceptions to the rule, but the rule should still be - avoid 'this.'


IMO the use of self as the name of the variable referencing to 'this' is a little dangerous, because in a function, if you miss the line var self = this, self is a valid reference to the window object, so you will not get an expected error for an undefined variable.


Better a little dangerous 'self' than a lot of dangerous 'this.'


There's no real danger difference. If you implicitly do:

    var self = this;
When you need to use `this`, you just end up proxying the problem behind a variable. If you don't understand what `this` is, it's not going to magically start correcting your misunderstanding.

The only possible value it might have is when nesting functions if you refer to `self` instead of `this` correctly, but this is the only case when I actually use this pattern, and when I first create a nested function that needs to use the parent's scope. Another option is to just `.bind(this)`.

In any case, I would disagree that one should try to avoid `this`. Instead, one should educate themselves to understand what things in the language they're using mean. And this subject is a great weeder question in Web Engineer position interviews. If a person can't implement `bind`, can't explain `call`/`apply`, what strict mode does, and what the `this` keyword means in most contexts, they probably aren't strong enough in JS. This is one of the most fundamental concepts in JS, it shouldn't be a source of confusion.


I understand 'this', that's why I avoid it. All languages have their quirks, and some programmers like to think they're 'smart' by taking advantage of those quirks. Then when you or your team members run into problems you blame the person and not the language, which you're right, but maybe you should of just kept your code simple in the first place.


I'm not sure I understand you.

Dereferencing things that aren't pointers leads to unexpected results in C. Should we avoid dereferencing any variable in C?

I think of `this` in the same way - it behaves normally if you use it the way it's intended to be used. In other words, don't try to read `this` in a callback without binding it first, and you'll be fine.

Or are there other pitfalls I'm unaware of?


Totally, just remember to use var. Dangerous this is much scary then accidentally adding a global variable.


The problem with `self` is that it is already a predefined global variable in the browser pointing to the global object :)

And btw, the `this` situation with javascript is not that bad :) use bind - either the Ecmascript 5 version or shimmed and you will be fine. Not much drama.


Well you might just use some other name than `self` e.g. I use var _this = this;


how about var that = this;


I'm confused... doesn't that just reduce the question to "what is the current binding of self"?


After assignment "self" does not change even if "this" does.

    var player = { 
        play: function(){
            var self = this;
            setTimeout(function(){
                console.log(self);
            });
        }
    }


No but this is still bound at call time, so `self` will be foo in foo.play() just as this, so that's basically the same thing + a standard variable assignment to get the setTimeout working.

On a sidenote, I'm trying to get away from using self, and do object.bind in all my setTimeouts. To my mind, it leads to much cleaner code.


object.bind doesn't look very clean compared to using self.


The nice thing about it is that it allows you to break out of nesting hell while still being prototyped... e.g. window.setTimeout(this.foo.bind(this), baz);

Since .bind is essentially currying, you could imaginably do window.setTimeout(this.foo.bind(this, bar), baz) as well, if foo is dependent on bar (timing out a request `bar' or something like that).


And in JS is really easy to create an abstraction for this issue:

    function wait(o){
      return setTimeout(function(){ o.action.call(o.this) },o.delay*1000)
    }    
To use it like this:

    wait({
       action : function(){ console.log(this) },
       delay : 1,       
       this : this
    });


If you're writing abstractions to avoid bad language features, maybe you shouldn't be using bad language features in the first place.


Bind is not supported in IE8 and older versions. You could use the underscore library or another one that haves a cross-browser bind method.


For my particular use case, IE8 is not an issue, but sure, a shim is like three lines, and we did use that before (when IE8 was an issue).


But you still have to be aware of the semantics of `this`:

    var play = player.play;
    play();
"self" will still strangely bind to "window" if not in strict mode.


Of course, taking the method out of the object will produce a different result. But if you are doing something weird like that you should know what you are doing.


I don't think it's _that_ weid considering JS is a language where functions are perfectly normal values. Someone unfamiliar with the semantics of "this" in JS might expect that:

    player[playing ? "play" : "pause"]()
(a not-so-weird JS pattern) would be the same as:

    (playing ? player.play : player.pause)()


Indeed --- Python goes to great lengths to work this way, automatically binding the self argument on method extraction.

    o.f()
Can be decomposed in Python:

    func = o.f
    func()
JavaScript fails this test, and it's a shame.


Ho, you beat me to it with the Python reference (see my other comment in this thread) :)

I don't know if it's really that bad that JS returns the unbound function when doing `obj.someFunc`, as it is consistent with any other property access: it returns the same thing that was assigned to that key in the object, or at some point in its prototype chain.

The really broken behavior, at least for me, is that when doing `var f = obj.someFunc(); f()`, the "this" gets bound to "window" in the f() call, instead of being null, or better yet raising an error whenever referenced, like an undefined variable.


It's actually a Javascript strength; one that makes it more dynamic.

    NodeList.prototype.map = Array.prototype.map
Just one line and now you can do stuff like:

    document.querySelectorAll("div").map(function(ele){
      return ele.id
    });


In what other language you have direct access to methods as first class objects to do an immediate execution of a method returned by an execution container (parenthesis in JS) ?

I think most people would write it like this:

    playing ? player.play() : player.pause()


> In what other language you have direct access to methods as first class objects to do an immediate execution of a method returned by an execution container (parenthesis in JS) ?

Not sure if i understand the question. This seems to work in Python:

    s = "Hello"
    up = True
    (s.upper if up else s.lower)()
Not that it's a very pythonic piece of code, but it works, and i would expect many other languages support something similar.

Anyway, that's tangential. And i wasn't discussion what "most people would" do either. What i was trying to say is that by aliasing "var self = this" you're not automatically immune to the quirks of "this" in JS.


For consistency and to avoid populating the scope with named variables, look at how jQuery works for good examples of how to use 'this' (the puns are infinite with this)


Thanks, I love having simple one liners to remember complex concepts :)


Articles like this become more and more important as more and more developers learn JS by way of frameworks like jQuery. If you want to understand "this". Understand call() and apply() first.


Thanks Henry!


Tltr; this is a mistake.




Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | DMCA | Apply to YC | Contact

Search: