This article is the first of two articles which explain the this
keyword in JavaScript. In this first post, we’ll look at what the value of this
will be under various invocation scenarios. In the second article, we’ll look at how these rules can be bent by the caller.
Before we begin, I’d like to go over two JavaScript concepts. It’s crucial these are understood to be able to grasp the behaviour of this
:
- The value of
this
within a function is not resolved until the function is called.This means that one function can be called 10 different times, and each time a different value ofthis
could be observed. - Whenever you name a function in JavaScript, what you actually do is create a named variable which references that function (i.e. the named variable points to the function). When you assign that named variable to another variable (i.e. alias the function), what you actually do is pass that reference by value, which leaves both variables pointing to the same function.
// "foo" points to a function which alerts "hi" function foo() { alert("Hi"); } // Set "b" to point to the same function as "foo" points to (which alerts "hi"). var b = foo; // "bar" points to a function which accepts a parameter which points to a function. // The body of the function simply executes the function provided. function bar(f) { f(); } // Pass the function pointed to be "foo" to the function pointed to by "bar". bar(foo);
This means you can have one function, which can be pointed to by multiple variables. If this is still unclear, perhaps the answers to this Stack Overflow question will help the point sink in.
Once you’ve understood the two concepts above, the rules as to what the value of this
is is quite straightforward; it’s the rule-bending we discuss in the next article that makes everything more more complicated!
- If you access the function via a property of an object, the value of
this
will be the object.var obj = { foo: function () { alert(this.bar); }, bar: 4 }; obj.foo(); // alerts "4", as `this` inside `foo()` is `obj`.
- If the variable you access the function from is just a normal variable, the value of
this
depends on whether you’re in strict mode. Unless you know otherwise, chances are you won’t be.- If you are in strict mode,
this
will beundefined
- If you aren’t in strict mode,
this
will the global object. In browsers, the global object iswindow
.
function foo() { "use strict"; alert(typeof this === "undefined"); } function bar() { alert(typeof this === "object" && this === window); } foo(); // alerts `true`, as inside strict mode, `this` is undefined bar(); // alerts `true`, as inside non-strict mode, this is the global (window) object
- If you are in strict mode,
That’s all there is to it! One common “got-cha” however is when you pass a function (which was pointed to by a object member) as a parameter to another function, people still expect this
to be the object.
// Define an object which has a property "foo", which points to a function.
// that function alerts "this.val".
var obj = {
foo: function () {
alert(this.val);
},
val: "bar"
};
// Remember, if the function we're calling is a member of an object, the value of `this` will be the object.
// In this case "this" resolves to "obj", so we see "bar" being alerted.
obj.foo();
// Lets define another function, which accepts a function as a parameter. All it does is call that function.
function baz(f) {
f();
}
// Now we pass the function pointed to by "obj.foo" to "baz()". "this" will now be the global object, and
// "this.val" will be undefined.
baz(obj.foo);
No! Don’t forget when you alias a function (either by assigning it to another variable or passing it as a parameter to another function), a new pointer to the same function is created. That new pointer has it’s own behaviour; and it’ll follow behaviour #2.
With the rules out of the way, lets go through some examples to see the rules being applied in practise;
function foo() {
console.log(this);
}
foo(); // This will be the global object ("window")
var obj = {
bar: foo
};
obj.bar(); // This will be "obj"
var baz = obj.bar;
baz(); // This will be the global object ("window") again.