The author critically misunderstands the way that `this` gets set, and in doing so has developed a significantly overcomplicated mental model. This is exemplified in the statement:
You can use this in any function on an object to refer to other properties on that object. This is not the same as an instance created with new. ... Note, there was no use of new , no Object.create and no function called to create obj.
This is exactly the same behavior that you see on an object method reference, and drawing any distinction at all makes understanding the behavior of this harder than it needs to be.
In nearly every case (with the exception of bind, call and apply) the this variable is set at the CALL SITE of the function, NOT THE DEFINITION. Note that in the three other cases, you still aren't setting this at the definition.
The reason you get this behaving as the author describes on objects created with new is only (as the author nearly, but not quite, realizes) because you called the function in the form:
thing.method()
Specifically, it is the object reference that sets the this context within the method call.
You can be sure that the object literal case is just the same. You don't need to call new to get an object reference that has a prototype chain: namely, an object literal is instantiated with the prototype Object. You can verify this is true with a simple:
I see that some people are slowly redefining the word "simple" as applied to programming languages. A 4-point bullet list that still requires knowing about typical caller behavior is hardly "simple".
Actually, it's not about typical caller behavior. When you write a function, what 'this' is is going to depend on how your function is called. If you don't want caller behavior to affect your function, don't use 'this'. If you want to allow callers to choose what object your function should act on using a variety of convenient syntaxes, then write your function to use 'this', but be aware of all the ways a caller might set it.
Exactly. Once you understand underlying concept, `this` is not complicated or weird.
Small addendum to your list: "whatever is left of the dot at call-time" is a nice way to explain "base reference" concept, but be wary of non-trivial constructs:
(f = thing.someFunction)(); // this references global object
(function(){return thing.someFunction }())(); // this references global object
eval('thing.someFunction')(); // this references global object
I don't think a project is "wrong" if it is working.
Also these a just opinions, but I think it's counterproductive.
I feel it's the same as class systems with inheritance and lots of OO properties implemented. It's not wrong, but it's so much overhead, so easy to shoot one's foot, for so few returns. And there's a class syntax coming to ES6, so I'm clearly in the minority.
For a discussion on the "this" use, there is an interesting talk[0] by D.Crockford on the subject, at about 8 min in (but I'd watch the whole thing). I don't agree with everything he is advising, but I think he has pretty compelling arguments overall.
There's a lot of things in js that are not necessary to write good code, and avoiding them can reduce a lot of the complexity IMO. Some people are OK with complexity and want expressivity, I'd prefer basic and predictable easily behaviours.
Then, you probably meant to call console.log after one second, not running it immediately and passing result to timeout argument, and passing 1000 as a callback argument.
The way you've written this code, `this` refers to `o`, as it has been evaluated at the time of execution of `f()` (first case) rather than inside the `setTimeout` callback, at which point it'd be `Window` (second case). So yes.
> The author critically misunderstands the way that `this` gets set...
I don't actually. (I'm the author) I know that the this key word is executed at run time to figure out what this is. It is true there are simpler ways to explain this and this blog post isn't that. I link to such a post at the bottom of my own. These are just some examples. There's also more to that blog post than what everyone is discussing, like returning from a constructor and global this in node.js and accessing this in eval and more. Anyway, I didn't submit this to HN and hadn't intended to. I also didn't intend to sit here tonight and put on a thick skin to deal with all this criticism for something I wrote months ago.
Don't take the criticism too hard. I enjoyed the post. I don't regularly write JavaScript so I found many points helpful. There is potential for pedantry here on HN, so dont take offense. I appreciate the post...
The article was a nice refresher about 'this' subtleties, but it put too much work into contrasting it against Java's this, instead of just laying out the actual JS rules.
To me, 'this' is JS is a lot more self explanatory than this is Java. And your article doesn't make that apparent. In JS this is just another object, like any other object. It follows the exact same scoping rules as every other object.
And the tragic thing about "this" being a magic keyword that looks up a dynamic value in the runtime, instead of a normal lexically scoped variable like it appears, is that 90% of the time "this" is exactly the thing you want a closure to close over, so you have to do ridiculous stuff like "var self = this;".
Even knowing this rule very well and being totally hard core OCO (Obsessive Compulsive ORDER, not Disorder) about the code, I still make "this" mistake ALL THE TIME, so whenever something weird happens, "this" mistakes are the first thing I look for.
"this" is such a terrible design flaw, it bites me even when I'm expecting it.
Patterns which could have been just as easily implemented using another more explicit technique for dynamic binding (like simply passing the dynamic parameter to the function as a normal argument) without fucking up the entire language design with invisible implicit magic, and dooming millions of programmers to repeatedly make difficult to detect, subtle mistakes, bugs and security holes.
Please give me one example of using "this" outside of lexical context, that is impossible or tangibly inconvenient to do by some other technique that JavaScript already supports.
Well, you could pass around the object but then you'd have to come up with a new object-literal syntax because `this` won't exist any more, unless you're planning on keeping it around but keeping it lexically scoped, in which case you have to stop using object literals as traits and prototypes.
At that point you might as well just redesign the language from scratch, which maybe you'd appreciate.
I agree. The fact that you have to write library code defensively to still work depending on how your method's call, or that you have to use an awkward bind to preserve the `this` context of a method is so annoying and error-prone to me.
Even when you remember to perform the awkward bind / Function.prototype.call / Function.prototype.apply / var self = this; you still may already be inside of another context where this is not correctly defined. So then your code LOOKS even more like it will work thanks to the cargo cult magic ceremonies you've performed perfectly, but in the wrong place. It's like going into the bathroom like you're supposed to, but then taking a shit on the floor right next to the toilet, then flushing.
You don't have to do that, you can tell a function what to use by using 'call' or 'apply'. Some people even use bind, but I think it adds too much line noise.
You don't have to do what? Make mistakes? Of course I don't have to make mistakes, but I do all the time, and so do other people.
You have to REMEMBER that you need to explicitly and inconveniently tell the function which "this" to use, by drilling down into Function.prototype.call/apply, even though it LOOKS like it will simply work like any other variable, which it doesn't.
Please consider that programmers are not perfect, and do not have infinite time or unlimited memory or perfectly focused and undistracted attention, and programming languages are user interfaces for imperfect humans, that should minimize the likelihood of errors, not set traps for less experienced programmers to make obvious mistakes, just so that more experienced programmers can gloat over how much smarter they are for having memorized every detail of the language spec. (-; I'm looking at you, automatically inserted semicolon! ;-)
And my point is that I AM an experienced programmer who has memorized every detail of the JavaScript language spec and uses it all the time, yet I STILL make "this" mistakes AGAIN and AGAIN. While on the other hand, I don't think I've ever made a "self" mistake with Python.
The way I think about it ('this' being like any other object), closures are just supplied a default 'this' object (window, in the case of web), and instead of providing a this argument via the argument list, you can choose it with the apply/call/bind functions.
I like your article and it fits well with my usual methodology of grokking something, namely running a bunch of tiny examples until I see the patterns.
Yes - the trick with JS is to realize it has this functional core, and every object has an __proto__ property, and then most of its syntax is syntactic sugar over the top of that.
new method()
is syntactic sugar for something like
var _tmp = {};
_tmp.__proto__ = method.prototype;
_tmp.call(method);
You can use this in any function on an object to refer to other properties on that object. This is not the same as an instance created with new. ... Note, there was no use of new , no Object.create and no function called to create obj.
This is exactly the same behavior that you see on an object method reference, and drawing any distinction at all makes understanding the behavior of this harder than it needs to be.
In nearly every case (with the exception of bind, call and apply) the this variable is set at the CALL SITE of the function, NOT THE DEFINITION. Note that in the three other cases, you still aren't setting this at the definition.
The reason you get this behaving as the author describes on objects created with new is only (as the author nearly, but not quite, realizes) because you called the function in the form:
Specifically, it is the object reference that sets the this context within the method call.You can be sure that the object literal case is just the same. You don't need to call new to get an object reference that has a prototype chain: namely, an object literal is instantiated with the prototype Object. You can verify this is true with a simple:
...and so forth.