JavaScript: Clarifying The Keyword ‘this’
henrycode.tumblr.com
henrycode.tumblr.com
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.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.
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 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.
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.
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]
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
I can't seem to go back and edit, though...
You can read more about it for instance here: http://www.thecssninja.com/javascript/handleevent
And thank you for 5), I didn't know it! :)
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.
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().
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 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'")
}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>
});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.
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'.
EDIT: I meant that as a pronoun.
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.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 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 1In 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.
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".
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. 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.
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?
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.
var player = {
play: function(){
var self = this;
setTimeout(function(){
console.log(self);
});
}
}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.
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).
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
}); var play = player.play;
play();
"self" will still strangely bind to "window" if not in strict mode. player[playing ? "play" : "pause"]()
(a not-so-weird JS pattern) would be the same as: (playing ? player.play : player.pause)() o.f()
Can be decomposed in Python: func = o.f
func()
JavaScript fails this test, and it's a shame.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.
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
});I think most people would write it like this:
playing ? player.play() : player.pause()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.