The JavaScript this
keyword is ubiquitous yet misconceptions abound.
What you need to know
Every execution context has an associated ThisBinding
whose lifespan is equal to that of the execution context and whose value is constant. There are three types of execution context: global, function and evaluation. Here’s a tabular summary followed by a little more detail, and some examples:
Execution Context | Syntax of function call | Value of this |
Global | n/a | global object (e.g. window ) |
Function | Method call:myObject.foo(); |
myObject |
Function | Baseless function call:foo(); |
global object (e.g. window )( undefined in strict mode) |
Function | Using call:foo.call(context, myArg); |
context |
Function | Using apply:foo.apply(context, [myArgs]); |
context |
Function | Constructor with new:var newFoo = new Foo(); |
the new instance (e.g. newFoo ) |
Evaluation | n/a | value of this in parent context |
1. Global context
this
is bound to the global object (window
in a browser)
alert(this); //window
2. Function context
There are at least 5 ways to invoke a function. The value of this
depends on the method of invocation
a) Invoke as a method
this
is the baseValue of the property reference
var a = { b: function() { return this; } }; a.b(); //a; a['b'](); //a; var c= {}; c.d = a.b; c.d(); //c
b) Invoke as baseless function call
this
is the global object (or undefined
in strict mode)
var a = { b: function() { return this; } }; var foo = a.b; foo(); //window var a = { b: function() { var c = function() { return this; }; return c(); } }; a.b(); //window
The same applies to self invoking functions:
var a = { b: function() { return (function() {return this;})(); } }; a.b(); //window
c) Invoke using Function.prototype.call
this
is passed by argument
d) Invoke using Function.prototype.apply
this
is passed by argument
var a = { b: function() { return this; } }; var d = {}; a.b.apply(d); //d
e) Invoke a constructor using new
this
is the newly created object
var A = function() { this.toString = function(){return "I'm an A"}; }; new A(); //"I'm an A"
3. Evaluation context
this
value is taken from the this
value of the calling execution context
alert(eval('this==window')); //true - (except firebug, see above) var a = { b: function() { eval('alert(this==a)'); } }; a.b(); //true;
What you might want to know
This section explores the process by which this
gets its value in the functional context – using ECMA-262 version 5.1 as a reference.
Lets start with the ECMAScript definition of this
:
this
keyword evaluates to the value of the ThisBinding
of the current execution context.from ECMA 5.1, 11.1.1
How is ThisBinding set?
Each function defines a [[Call]] internal method (ECMA 5.1, 13.2.1 [[Call]]) which passes invocation values to the function’s execution context:
1. If the function code is strict code, set the ThisBinding to thisValue.
2. Else if thisValue is null or undefined, set the ThisBinding to the global object.
3. Else if Type(thisValue) is not Object, set the ThisBinding to ToObject(thisValue).
4. Else set the ThisBinding to thisValue
from ECMA 5.1, 10.4.3 Entering Function Code (slightly edited)
In other words ThisBinding
is set to the object coercion of the abstract argument thisValue
, or if thisValue
is undefined, the global object (unless running in strict mode in which case thisValue
is assigned to ThisBinding
as-is)
So where does thisValue
come from?
Here we need to go back to our 5 types of function invocation:
1. Invoke as a method
2. Invoke as baseless function call
in ECMAScript parlance these are Function Calls and have two components: a MemberExpression and an Arguments list.
2. Let func be GetValue(ref).
6. If Type(ref) is Reference, then
a. If IsPropertyReference(ref) is true
i. Let thisValue be GetBase(ref).
b. Else, the base of ref is an Environment Record
i. Let thisValue be the result of calling the ImplicitThisValue concrete method of GetBase(ref).
8. Return the result of calling the [[Call]] internal method on func, providing thisValue as the this value and
providing the list argList as the argument values
from ECMA 5.1, 11.2.3 Function Calls
So, in essence, thisValue
becomes the baseValue of the function expression (see step 6, above).
In a method call the function is expressed as a property, so the baseValue
is the identifier preceding the dot (or square bracket).
foo.bar(); //foo
assigned to thisValue
foo[‘bar’](); //foo
assigned to thisValue
var foo = { bar:function() { //(Comments apply to example invocation only) //MemberExpression = foo.bar //thisValue = foo //ThisBinding = foo return this; } }; foo.bar(); //foo
A baseless function is either a function declaration or a variable – in either case the baseValue
is the Environment Record (specifically a Declarative Environment Record). ES 5.1, 10.2.1.1.6 tells us that the ImplcitThisValue
of a Declarative Environment Record is undefined.
Revisiting 10.4.3 Entering Function Code (see above) we see that unless in strict mode, an undefined thisValue
results in a ThisBinding
value of global object. So this
in a baseless function invocation will be the global object. In strict mode the ThisBinding
remains undefined.
In full…
var bar = function() { //(Comments apply to example invocation only) //MemberExpression = bar //thisValue = undefined //ThisBinding = global object (e.g. window) return this }; bar(); //window
3. Invoke using Function.prototype.apply
4. Invoke using Function.prototype.call
(specifications at 15.3.4.3 Function.prototype.apply and 15.3.4.4 Function.prototype.call)
These sections describe how, in call and apply invocations, the actual value of the function’s this argument (i.e. its first argument) is passed as the thisValue to 10.4.3 Entering Function Code. (Note this differs from ECMA 3 where primitive thisArg values undergo a toObject transformation, and null or undefined values are converted to the global object – but the difference will normally be negligible since the value will undergo identical transformations in the target function invocation (as we’ve already seen in 10.4.3 Entering Function Code))
5. Invoke a constructor using new
1. Let obj be a newly created native ECMAScript object.
8. Let result be the result of calling the [[Call]] internal property of F, providing obj as the thisValue and providing the argument list passed into [[Construct]] as args.
10. Return obj.
from ECMA 5.1, 13.2.2 [[Construct]]
This is pretty clear. Invoking the constructor with new
creates an object that gets assigned as the thisValue. It’s also a radical departure from any other usage of this
.
House Cleaning
Strict mode
In ECMAScript’s strict mode, the thisValue
is not coerced to an object. A
this
value of null
or undefined
is not converted to the global object and primitive values are not converted to wrapper objects
The bind function
Function.prototype.bind
is new in ECMAScript 5 but will already be familiar to users of major frameworks. Based on call/apply it allows you to prebake the thisValue
of an execution context using simple syntax. This is especially useful for event handling code, for example a function to be invoked by a button click, where the ThisBinding
of the handler will default to the baseValue
of the property being invoked – i.e. the button element:
//Bad Example: fails because ThisBinding of handler will be button var sorter = { sort: function() { alert('sorting'); }, requestSorting: function() { this.sort(); } } $('sortButton').onclick = sorter.requestSorting;
//Good Example: sorter baked into ThisBinding of handler var sorter = { sort: function() { alert('sorting'); }, requestSorting: function() { this.sort(); } } $('sortButton').onclick = sorter.requestSorting.bind(sorter);
Further Reading
ECMA 262, Edition 5.1
11.1.1 Definition of this
10.4.3 Entering Function Code
11.2.3 Function Calls
13.2.1 [[Call]]
10.2.1.1 Declarative Environment Record (ImplicitThisValue)
11.1.1 [[Construct]]
15.3.4.3 Function.prototype.apply
15.3.4.4 Function.prototype.call
15.3.4.5 Function.prototype.bind
Annex C The Strict Mode of ECMAScript
Good overview, congrats.
In addition, don’t forget some subtle cases such when seems that we have your case (a) Invoke a property, but thisValue isn’t set to base of reference, because there is no reference:
[js]
foo.bar(); // foo
(foo.bar)(); // foo
(foo.bar = foo.bar)(); // global
(foo.bar || foo.baz)(); // global
(function () {})(); // global
[/js]
Dmitry.
Hi Dmitry – thanks for your feedback.
These are good examples – I covered self-invoking functions already – I will try to incorporate the others
Gotta catch ’em all! And you did… :p and Dmitry catched pretty much all the edge cases around. Another nice article covering a complex JS/ES matter.
One of the common stumbles for new JavaScripters 🙂
… and also why I prefer closures to objects. It’s just generally more concise and robust (don’t need to worry about what context the function might be called in eventually) if you use closures instead of properties of `this`.
Personally, I prefer this version of your example:
Keep these articles coming Angus 🙂
Yep totally agree. Closures ftw! Sometimes the value of this seems to verge on arbitrary. Google “javascript this keyword”: nobody can really encapsulate the meaning without meandering off into edge cases.
Peter Michaux includes ‘this’ as a JavaScript warning word
http://peter.michaux.ca/articles/javascript-warning-words
and wrote a nice piece about this free widget definition
http://peter.michaux.ca/articles/javascript-widgets-without-this
Thanks for helping de-voodoofying this 🙂
I think I’ll be making references to functions instead of blindly doing .bind(this) – it’s measureably faster even in Chrome (with the new native bind)
I would also add that `with` statement — quite expectedly — makes for confusing behavior when it comes to `this` value:
For example, normally you would think that `foo()` invokes `foo` with `this` value referencing Global object; however, when executed as part of a `with` statement, `this` might well reference an object inserted into the scope chain.
This is the clearest and most thought through explanation I’ve seen so far.
Good job on this one!
Is this complexity intentional?
If yes, what are the use cases they wanted to address?
I really can’t understand why someone wanted such a mess to be in a programming language!
Hi, syntax error in line 1 in the third type of execution context(Evaluation context): missing right parenthesis.
correct
alert(eval('this==window'));
thanks – fixed!
In example 2 E, is the last line supposed to have a comment?
Perhaps
new A(); "I'm an A"
should be
new A(); //"I'm an A"
Also, do you have an example for 2 C?
Thanks for the article!
Hi Bob
Yeah thanks for pointing that out – fixed now!
2c is almost identical to 2d
Thanks for the great rundown.
One error I ran into: there should be a semicolon after the closing brace on 2b line 14.
Glad you like it Rod – and thanks for the correction!
excellent post!
Reblogged this on Ateev and commented:
A great explanation . !
Howdy! I know this is somewhat off topic but I was wondering if you knew where I could locate a captcha plugin for my comment form?
I’m using the same blog platform as yours and I’m having problems finding one?
Thanks a lot!