JavaScript Lists --- This
Originally posted on 15 January 2013.
This post was inspired by a recent thread on Hacker News that created some conversation about the this keyword in JavaScript. I want to thank Henry for writing that post and laying out the base cases that I built on for this post.JavaScript has some confusing features. Every now and then, I see
blog posts detailing “how to understand closures in JavaScript” or
“grokking closures in JavaScript.” Most of these are written, quite
appropriately, by folks who are learning about JavaScript's features and
sharing their knowledge as they learn useful tricks and build up an
understanding of the language. As a result, these posts are often
incomplete. Now, you can go to the JavaScript spec if you want a more
detailed answer to your question, but the spec isn't nicely laid out as
"here are all the ways this
could be bound" and "here's how
variable binding (doesn't) work in JavaScript."
I wish there was a resource that laid out features this way, so I'm going to try writing down a few lists of what I think are comprehensive descriptions of some features of JavaScript. I'll happily update the post (with credit) when folks find stuff that I forgot to add to the list. I'll try to order things so that corner-case uses come later in the list than the most frequent uses, but there will be some random judgment calls made by me in there. Without further ado...
this
There are lots of ways to get the keyword this
bound in
JavaScript.
In a method invocation expression, the result of evaluating the
expression to the left of the dot or bracket will be bound as
this
inside the called method:
o = { x:22, fun: function() { return this.x; } };
o.fun(); // returns 22
o.["f" + "un"](); // returns 22
function return_o() { return o; }
return_o().fun(); // returns 22
If you call a method with function call syntax, even if it used to be a field of an object, one of two things will happen:
window
passed to
it as this
(it's actually just "the global object," which
is a different thing if you're on the server running Node, but the
common case is window
in the browser).
window.x = 42;
o = { x:22, fun: function() { return this.x; } }
extracted = o.fun;
extracted(); // returns 42, not 22
undefined
will be bound to
this
(the "set to
global" option is skipped):
window.x = 42;
o = { x:22, fun: function() { "use strict"; return typeof this; } }
extracted = o.fun;
extracted(); // returns "undefined"
When you pass callbacks to built-in ECMAScript functions like
forEach
, you can supply a thisArg
, and if you don't, this
will be bound to undefined
(reference).
function f() { console.log(String(this)); }
[1,2].forEach(f); // prints undefined, undefined
[1,2].forEach(f, "mythisarg"); // prints "mythisarg" "mythisarg"
Some callbacks, like setInterval
and
setTimeout
, pass the global object rather than
undefined
. ECMAScript-defined functions have been
specified to take and explicit this argument or pass undefined, but the
DOM APIs (to my knowledge) still usually pass the global object when
there is no DOM node involved.
function f() { console.log(this); }
setTimeout(f,0); // prints the global object
Other callbacks, like those registered on DOM nodes themselves, pass
the node as the this argument
.
document.addEventListener('click', function() { console.log(this); })
// prints the document object when you click on the page
It's nice to think of this case as the browser doing a method invocation for you, with the DOM element to the left of the dot.
When using this at the toplevel (which is allowed), it is bound to the
global object (even in strict mode, it's just the default global this
binding ):
console.log(this) // prints the window object
new
When you use the new
keyword, this
is bound to
a newly-created object with its internal proto
set to the
prototype
field of the constructor.
function f() {
console.log(Object.getPrototypeOf(this) === f.prototype);
}
new f(); // prints true
The builtins Function.prototype.call/apply/bind
allow
you to provide your own this
argument to a function:
function f() { console.log(String(this)); }
f.call("mythis", "other", "args"); // prints "mythis"
f.apply("mythis", []); // prints "mythis"
f.bind("mythis")(); // prints "mythis"
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 sort of matches the method invocation rule
except for the fact that "method call" is implicit; there are no () in
the expression o.x, but it may call a getter function for x that passes
o as this (reference).
o = { foo: 42,
bar: "another field",
get x() { console.log(this.foo); },
set x(_) { console.log(this.bar); } };
o.x; // prints 42
o.x = "whatever"; // prints "another field"
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 (this is why I have calls to String
in
a few examples to make them print the way I wanted):
Number.prototype.identity = function() { return this; }
var x = 5
var x2 = x.identity()
typeof x2 === 'object' // (true!)
typeof x2 === 'number' // (false!)
Again, strict mode gives us saner behavior. 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)
My next list will probably be a list of ways variables can be bound in JavaScript, but I'm open to other suggestions. Contact me if you'd like to see something else or want to report a bug in this list.