Read this article in: Russian.
Introduction
In this article we will discuss one more detail directly related with execution contexts. The topic of discussion is the this
keyword.
As the practice shows, this topic is difficult enough and often causes issues in determination of this
value in different execution contexts.
Many programmers are used to thinking that the this
keyword in programming languages is closely related to the object-oriented programming, exactly referring the newly created object by the constructor. In ECMAScript this concept is also implemented, however, as we will see, here it is not limited only to definition of created object.
Let’s see in detail what exactly this
value is in ECMAScript.
Definitions
this
is a property of the execution context. It’s a special object in which context a code is executed.
activeExecutionContext = { VO: {...}, this: thisValue };
where VO is variable object which we discussed in the previous chapter.
this
is directly related to the type of executable code of the context. The value is determined on entering the context and is immutable while the code is running in the context.
Let’s consider these cases more in detail.
This value in the global code
Here everything is simple enough. In the global code, this
value is always the global object itself. Thus, it is possible to reference it indirectly:
// explicit property definition of // the global object this.a = 10; // global.a = 10 console.log(a); // 10 // implicit definition via assigning // to unqualified identifier b = 20; console.log(this.b); // 20 // also implicit via variable declaration // because variable object of the global context // is the global object itself var c = 30; console.log(this.c); // 30
This value in the function code
Things are more interesting when this
is used in function code. This case is the most difficult and causes many issues.
The first (and, probably, the main) feature of this
value in this type of code is that here it is not statically bound to a function.
As it has been mentioned above, this
value is determined on entering the context, and in case with a function code the value can be absolutely different every time.
However, at runtime of the code this
value is immutable, i.e. it is not possible to assign a new value to it since this is not a variable (in contrast, say, with Python programming language and its explicitly defined self
object which can repeatedly be changed at runtime):
var foo = {x: 10}; var bar = { x: 20, test: function () { console.log(this === bar); // true console.log(this.x); // 20 this = foo; // error, can't change this value console.log(this.x); // if there wasn't an error, then would be 10, not 20 } }; // on entering the context this value is // determined as "bar" object; why so - will // be discussed below in detail bar.test(); // true, 20 foo.test = bar.test; // however here this value will now refer // to "foo" – even though we're calling the same function foo.test(); // false, 10
So what affects the variations of this
value in function code? There are several factors.
First, in a usual function call, this
is provided by the caller which activates the code of the context, i.e. the parent context which calls the function. And the value of this
is determined by the form of a call expression (in other words by the form how syntactically the function is called).
It is necessary to understand and remember this important point in order to be able to determine this
value in any context without any problems. Exactly the form of a call expression, i.e. the way of calling the function, influences this
value of a called context and nothing else.
(as we can see in some articles and even books on JavaScript which claim that “this
value depends on how function is defined: if it is global function then this
value is set to global object, if function is a method of an object this
value is always set to this object” — what is mistaken description). Moving forward, we see that even normal global functions can be activated with different forms of a call expression which influence a different this
value:
function foo() { console.log(this); } foo(); // global console.log(foo === foo.prototype.constructor); // true // but with another form of the call expression // of the same function, this value is different foo.prototype.constructor(); // foo.prototype
It is similarly possible to call the function defined as a method of some object, but this
value will not be set to this object:
var foo = { bar: function () { console.log(this); console.log(this === foo); } }; foo.bar(); // foo, true var exampleFunc = foo.bar; console.log(exampleFunc === foo.bar); // true // again with another form of the call expression // of the same function, we have different this value exampleFunc(); // global, false
So how does the form of the call expression influences this
value? In order to fully understand the determination of the this
value, it’s necessary to consider in detail one of the internal types — the Reference
type.
Reference type
Using pseudo-code the value of Reference
type can be represented as an object with two properties: base (i.e. object to which a property belongs) and a propertyName in this base:
var valueOfReferenceType = { base: <base object>, propertyName: <property name> };
strict
— the flag whether a reference is resolved in the strict mode.
'use strict'; // Access foo. foo; // Reference for `foo`. const fooReference = { base: global, propertyName: 'foo', strict: true, };
Value of Reference
type can be only in two cases:
- when we deal with an identifier;
- or with a property accessor.
Identifiers are handled by the process of identifiers resolution which is in detail considered in the Chapter 4. Scope chain. And here we just notice that at return from this algorithm always there is a value of Reference
type (it is important for this
value).
Identifiers are variable names, function names, names of function arguments and names of unqualified properties of the global object. For example, for values on following identifiers:
var foo = 10; function bar() {}
in intermediate results of operations, corresponding values of Reference
type are the following:
var fooReference = { base: global, propertyName: 'foo' }; var barReference = { base: global, propertyName: 'bar' };
For getting the real value of an object from a value of Reference
type there is GetValue
method which in a pseudo-code can be described as follows:
function GetValue(value) { if (Type(value) != Reference) { return value; } var base = GetBase(value); if (base === null) { throw new ReferenceError; } return base.[[Get]](GetPropertyName(value)); }
where the internal [[Get]]
method returns the real value of object’s property, including as well analysis of the inherited properties from a prototype chain:
GetValue(fooReference); // 10 GetValue(barReference); // function object "bar"
Property accessors are also know; there are two variations: the dot notation (when the property name is correct identifier and is in advance known), or the bracket notation:
foo.bar(); foo['bar']();
On return of intermediate calculation we also have the value of Reference
type:
var fooBarReference = { base: foo, propertyName: 'bar' }; GetValue(fooBarReference); // function object "bar"
So, how a value of Reference
type is related with this
value of a function context? — in the most important sense. The given moment is the main of this article. The general rule of determination of this
value in a function context sounds as follows:
The value of this
in a function context is provided by the caller and determined by the current form of a call expression (how the function call is written syntactically).
If on the left hand side from the call parentheses ( ... )
, there is a value of Reference
type then this
value is set to the base object of this value of Reference
type.
In all other cases (i.e. with any other value type which is distinct from the Reference
type), this
value is always set to null
. But since there is no any sense in null
for this
value, it is implicitly converted to global object.
Let’s show on examples:
function foo() { return this; } foo(); // global
We see that on the left hand side of call parentheses there is a Reference
type value (because foo is an identifier):
var fooReference = { base: global, propertyName: 'foo' };
Accordingly, this
value is set to base object of this value of Reference
type, i.e. to global object.
Similarly with the property accessor:
var foo = { bar: function () { return this; } }; foo.bar(); // foo
Again we have the value of type Reference
which base is foo
object and which is used as this
value at bar
function activation:
var fooBarReference = { base: foo, propertyName: 'bar' };
However, activating the same function with another form of a call expression, we have already other this
value:
var test = foo.bar; test(); // global
because test
, being the identifier, produces other value of Reference
type, which base (the global object) is used as this
value:
var testReference = { base: global, propertyName: 'test' };
Note, in the strict mode of ES5 this
value is not coerced to global object, but instead is set to undefined
.
Now we can precisely tell, why the same function activated with different forms of a call expression, has also different this
values — the answer is in different intermediate values of type Reference
:
function foo() { console.log(this); } foo(); // global, because var fooReference = { base: global, propertyName: 'foo' }; console.log(foo === foo.prototype.constructor); // true // another form of the call expression foo.prototype.constructor(); // foo.prototype, because var fooPrototypeConstructorReference = { base: foo.prototype, propertyName: 'constructor' };
Another (classical) example of dynamic determination of this
value by the form of a call expression:
function foo() { console.log(this.bar); } var x = {bar: 10}; var y = {bar: 20}; x.test = foo; y.test = foo; x.test(); // 10 y.test(); // 20
Function call and non-Reference type
So, as we have noted, in case when on the left hand side of call parentheses there is a value not of Reference
type but any another type, this
value is automatically set to null
and, as consequence, to the global object.
Let’s consider examples of such expressions:
(function () { console.log(this); // null => global })();
In this case, we have function object but not object of Reference
type (it is not the identifier and not the property accessor), accordingly this
value finally is set to global object.
More complex examples:
var foo = { bar: function () { console.log(this); } }; foo.bar(); // Reference, OK => foo (foo.bar)(); // Reference, OK => foo (foo.bar = foo.bar)(); // global? (false || foo.bar)(); // global? (foo.bar, foo.bar)(); // global?
So, why having a property accessor which intermediate result should be a value of Reference
type, in certain calls we get for this
value not the base object (i.e. foo
) but global?
The matter is that last three calls, after applying of certain operations, have already on the left hand side of call parentheses the value not of Reference type.
With the first case all is clear – there unequivocally Reference
type and, as consequence, this
value is the base object, i.e. foo
.
In the second case there is a grouping operator which does not apply, considered above, method of getting the real value of an object from value of Reference
type, i.e. GetValue
(see note of 11.1.6). Accordingly, at return from evaluation of the grouping operator — we still have a value of Reference
type and that is why this
value is again set to the base object, i.e. foo
.
In the third case, assignment operator, unlike the grouping operator, calls GetValue
method (see step 3 of 11.13.1). As a result at return there is already function object (but not a value of Reference
type) which means that this
value set to null
and, as consequence, to global.
Similarly with the fourth and fifth cases — the comma operator and logical OR expression call the GetValue
method and accordingly we lose value of type Reference
and get value of type function; and again this
value is set to global.
Reference type and null this value
There is a case when call expression determines on the left hand side of call parentheses the value of Reference
type, however this
value is set to null
and, as consequence, to global. It is related to the case when the base object of Reference
type value is the activation object.
We can see this situation on an example with the inner function called from the parent. As we know from the second chapter, local variables, inner functions and formal parameters are stored in the activation object of the given function:
function foo() { function bar() { console.log(this); // global } bar(); // the same as AO.bar() }
The activation object always returns as this
value — null
(i.e. pseudo-code AO.bar()
is equivalent to null.bar()
). Here again we come back to the described above case, and again, this
value is set to global object.
The exception can be with a function call inside the block of the with
statement in case if with object contains a function name property. The with
statement adds its object in front of scope chain i.e. before the activation object. Accordingly, having values of type Reference
(by the identifier or a property accessor) we have base object not as an activation object but object of a with
statement. By the way, it relates not only to inner, but also to global functions because the with
object shadows higher object (global or an activation object) of the scope chain:
var x = 10; with ({ foo: function () { console.log(this.x); }, x: 20 }) { foo(); // 20 } // because var fooReference = { base: __withObject, propertyName: 'foo' };
The similar situation should be with calling of the function which is the actual parameter of the catch
clause: in this case the catch
object is also added in front of scope chain i.e. before the activation or global object. However, the given behavior was recognized as a bug of ECMA-262-3 and is fixed in the new version of standard — ECMA-262-5. I.e. this
value in the given activation should be set to global object, but not to catch
object:
try { throw function () { console.log(this); }; } catch (e) { e(); // __catchObject - in ES3, global - fixed in ES5 } // on idea var eReference = { base: __catchObject, propertyName: 'e' }; // but, as this is a bug // then this value is forced to global // null => global var eReference = { base: global, propertyName: 'e' };
The same situation with a recursive call of the named function expression (more detailed about functions see in Chapter 5. Functions). At the first call of function, base object is the parent activation object (or the global object), at the recursive call — base object should be special object storing the optional name of a function expression. However, in this case this
value is also always set to global:
(function foo(bar) { console.log(this); !bar && foo(1); // "should" be special object, but always (correct) global })(); // global
This value in function called as the constructor
There is one more case related with this
value in a function context — it is a call of function as the constructor:
function A() { console.log(this); // newly created object, below - "a" object this.x = 10; } var a = new A(); console.log(a.x); // 10
In this case, the new operator calls the internal [[Construct]] method of the A
function which, in turn, after object creation, calls the internal [[Call]] method, all the same function A
, having provided as this
value newly created object.
Manual setting of this value for a function call
There are two methods defined in the Function.prototype
(therefore they are accessible to all functions), allowing to specify this
value of a function call manually. These are apply
and call
methods.
Both of them accept as the first argument this
value which is used in a called context. A difference between these methods is insignificant: for the apply
the second argument necessarily should be an array (or, the array-like object, for example, arguments
), in turn, the call
method can accept any arguments; obligatory arguments for both methods is only the first — this
value.
Examples:
var b = 10; function a(c) { console.log(this.b); console.log(c); } a(20); // this === global, this.b == 10, c == 20 a.call({b: 20}, 30); // this === {b: 20}, this.b == 20, c == 30 a.apply({b: 30}, [40]) // this === {b: 30}, this.b == 30, c == 40
Conclusion
In this article we have discussed features of the this
keyword in ECMAScript (and they really are features, in contrast, say, with C++ or Java). I hope article helped to understand more accurately how this
keyword works in ECMAScript. As always, I am glad to answer your questions in comments.
Additional literature
10.1.7 – This;
11.1.1 – The this keyword;
11.2.2 – The new operator;
11.2.3 – Function calls.
Translated by: Dmitry A. Soshnikov with help of Stoyan Stefanov.
Published on: 2010-03-07
Originally written by: Dmitry A. Soshnikov [ru, read »]
With additions and corrections by: Zeroglif
Originally published on: 2009-06-28; updated on: 2010-03-07
Thank you,I have finisehd this article.
ECMA-262-3 深入解析.第三章.this
@denisdeng
OK, well done, Denis. I added a link to your translation.
Thank you!
Thanks a lot for this wonderful series of articles!
hi , one question:
But since there is no any sense in null for this value, it is implicitly converted to global object.
Can I understand like this:
The actual value of “this” does not exist null situation.
When this value is null ,then the value will be implicitly converted to the global object.
another question:
…the form of a call expression …
I think the “expression” means “FunctionExpression” in the previous chapter
right?
@Yonatan
thanks.
@justin
Yup, that’s right.
Nope, a FunctionExpression is an expression which defines special type of function.
And by “form of a call expression” I mean the view by which function context is activated:
So, it is how (in which view) function is activated. That exactly the one of the main factors influence this value.
Dmitry.
thanks!
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Nope, a FunctionExpression is an expression which defines special type of function.
And by “form of a call expression” I mean the view by which function context is activated:
function F() {}
// one view – “F” of call expression
F();
// another view – “F.prototype.constructor”
// of call expression for the same function
F.prototype.constructor();
So, it is how (in which view) function is activated. That exactly the one of the main factors influence this value.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
so,I translaste “form of a call expression”to chinese is “Way of calling the function”(调用函数的方式).
“form of a call expression” is too abstract, difficult to understand.
What do you think?
btw:”exactly referring the the newly created object by the constructor. ” , I think maybe you want to only one “the”.
@justin
Yep, I think the “way of calling the function” is a good clarification. I added this phrase also.
Yep, it was a typo, thanks; fixed.
Dmitry.
thanks!
I’m not very smart, I still have some other questions:
1.In the third case, assignment operator, unlike the grouping operator, calls GetValue method.
“calls GetValue method” ,why and where I can kown ” assignment operator will >calls GetValue method.”
2.The activation object always returns as this value — null (i.e. pseudo-code AO.bar() is equivalent to null.bar()).
AO is always null?why?
@justin
See 11.13.1 Simple Assignment ( = ), step 3. I added it to the article also.
See 10.1.6 Activation Object. The last sentence:
If you read attentively Chapter 2. Variable object, you should know that inner function declarations are stored in the activation object (AO) of the parent function:
So when bar function is called inside the foo function, the intermediate Reference type value is:
And then we see definition from the spec, that in such case should be used null instead of AO(bar).
Dmitry.
thanks a lot ~
about 2, I know “inner function declarations are stored in the activation object (AO) of the parent function”,but I don’t know “10.1.6 Activation Object. The last sentence:“; so,It’s too difficult to me to understand that.
about 1,if I read “11.13.1 Simple Assignment ( = ), step 3” in advance,It’s easy enough to understand.
there is another question:
if it is nessary for me to read ECMA-262-3 firstly,and then to read this set of articles? or could you do our a favour and add comments in details to the content which are from ECMA-262-3 in your articles.
your help would be great appreciated!
@justin
I didn’t understand completely this sentence. Is it clear now, or it still difficult to understand? If still, ask again, I’ll explain more detailed.
No, of course not. This series is exactly detailed explanation of the ECMA-262-3 specification. If you have already read the spec — it is good. But if not yet — it isn’t required strictly because many explanations are in these articles, and presented in more “human-view” (i.e. should be easier to understand than standard), but not just dry theoretical algorithms descriptions from the spec.
You of course can read specification simultaneously with reading this series.
Actually, many parts in this series are from the ECMA-262-3, and, repeat, this series — is exactly detailed explanation of the ECMA-262-3. So some related sections of the specification I always mention in the last section of every article — in the section of additional literature. But, yeah, I’ll try to put more references to the specification directly within content. Moreover, all vaguenesses can be clarified in comments and then, if needed, I’ll add references to the spec directly in-place.
Dmitry.
hi,Dmitry
thanks for your reply!
It’s clear now,I think my express of “about 2” make your misleading,my mean is that I don’t know what“10.1.6 Activation Object. The last sentence:“ is before,because I didn’t konw it from ECMA-262-3, but I could understand it after your explaination.
however,I think “more references to the specification directly within content” is great help for us who didn’t read ECMA-262-3.
I finally finished this chapter translation:http://www.cnblogs.com/justinw/archive/2010/05/04/1727295.html
btw:
“The same situation with a recursive call of the named function expression”
named function expression links to the wrong address
@justin
Well done; added your translation.
Yep, thanks; fixed.
Dmitry.
can you help me explain f1,f3,f4 why value is 50,20,50?
I am a little confused.
@leoner
In order to be able to answer this question precisely, you have to learn first the topic about variable object and stages of its modifications, i.e. — entering the context and the code execution stage.
Therefore, on entering the
fn
‘s function context, created three function declarations:f1
,f3
andf4
. This code can be transformed to:Function
f1
andf3
then are equivalent. Both use this value as a prefix forx
property.Then you know from corresponding section of the current article, that if the base component of a Reference type value is an activation object, then should be used null => global as this value.
All three (f1, f3, f4) are stored in the activation object of the fn function and corresponding reference value for all them is:
The same for f3 and f4. That means that this value at f1, f3 and f4 activations — is the global object. And accordingly
this.x
leads to 20 — i.e. thex
variable value defined in the global context.So the correct answer for f1 and f3 is 20.
In contrast, f4 doesn’t use
this.x
, but justx
. Where this name bindingx
should be resolved? It depends on scope chain lookup (for detailed explanation see Chapter 4. Scope chain).If f4 will be
executed(update: not executed, but defined; see explanation below. It’s only in Firefox, f4 is a function statement!) outside thewith
statement, thenx
name binding is resolved in the global object and is 20. You can check executing it before or afterwith
statement.But in your example f4 executes inside the(update: also, f4 has nothing with scope chain of fn!). And you should know (from the 4th chapter) thatwith
statementwith
adds its object in the front of scope chain. I.e. scope chain of thefn
function context is now looks like:So calling
f4
inside with,x
variable will be resolved to 50, because will be found earlier than global’s 20. And that’s correct answer for yourf4
is 50.Note however, if you create a function expression then
x
will be resolved always to 50 even outside thewith
:Because functions in JS are closures and use lexical (static) scoping, i.e. saves parent scope chain at creation of the function.
And
f2
funciton being a function expression in contrast with function declarations is created at code execution stage and is a property ofwith
‘s statement object.Therefore, a reference type value for
f2
is:And accordingly, withObject is used as this value at
f2
activation. And you setx
as 50 forwith
‘s object. Therefore, the correct answer forf2
is 50.Dmitry.
Thank you for you replay,I understand.:)
hi,Dmitry
when i read the ecma 10.4.3 Entering Function Code ,it says “The following steps are performed when control enters the execution context for function code contained in function object F, a caller provided thisArg, and a caller provided argumentsList: ”
i don’t understand the caller and the thisArg.
could you explain what they mean
thanks
@tiger
A caller — is a context which invokes (calls) some other context.
If we have foo function and call it from the global context, then the global context is the caller of the foo.
You can test a caller value via the non-standard (and deprecated) property with the same name of a function object, e.g. foo.caller, or a property of the arguments inside a function — arguments.caller. In SpiderMonkey for the global context a caller is null for other — a function from which a particular function is being called:
Accordingly, if we call the same function from some other context, a caller will be other:
In turn, a context which is being called is a callee. That’s why arguments.callee at execution of a function refers to itself — because it’s currently being called by a caller and is a callee.
Notice, if you’ll test this code in Firebug console, in the first example a caller is a context of the evaluation console’s code, but not the global.
So if some context (a caller) calls another context (a callee), then exactly a caller provides a this value and argument values for a function context.
This value is provided via the form of a call expression, i.e. the way of how (the same) function is executed:
Dmitry.
Wow, amazing article, thanks.
Its turns out that now I am JS Rebel, I dont believe anymore
in any book(lol), just on specifications!!
can you help me explain why this equal input?
think you!
@huoyue2010
Because when
aa
function is activated by theclick
event, thethis
value is bound to the input element. Always remember, thatthis
value is bound dynamically exactly at activation moment.It’s the same as it would be (in a very simplified manner):
Dmitry.
Superb articles!
one qs about the explanation for the comment from ‘leoner’ (above),
when I ran it chrome, it gave
I don’t really get hold this.
Why did it print 20 ?
@sojin, what a my mistake in that reply to leoner! Thanks for figuring it out. I’ve edited the previous reply with striking the wrong part of explanation.
It’s not completely wrong of course; it’s correct for exactly Firefox (in which console seems I was testing the code).
As you probably know Firefox defines a special type of functions which are called Function Statements. This extension allows to define functions conditionally in e.g.
if
-blocks. In all other current implementations a function declaration is independent from the conditionalif
-block.So in case of that example
f4
as shown in the reply to leoner is hoisted to the top and saves the parent scope chain at the moment of its definition. At this moment of course there is no that{x: 5}
added by thewith
.And in Chrome (and other) the behavior is correct. It should be
20
in the last call. But, in Firefox the behavior is also correct — depending on its Function Statement extension (which behaves similarly in this respect to Function Expressions as shown in that reply). And in FF it should be50
in the last call.The future version of ECMAScript should standardize function statements, and then the correct result should be as in FF, i.e.
50
. But now, both are correct. Since function declarations in blocks (which are function statements) are not defined by the current spec and are extensions with their own behavior. Though, the behavior of FF seems more logical to me.Dmitry.
Perfect! thanks.
Hello Dmitry,
First of all, thank you for so interesting series of articles. It is absolutely unique and definitely deserves to be published.
I am reading this chapter and specifically work on this pieces of code:
I realize that the code above is partially a pseudo-code, but I am still trying to
to modify it and run to see in action what is actually going on here.
I am not sure how to interpret the “base:global” expression in both
fooReference
andbarReference
variables.GetValue()
function is also kind of vaguely defined.Can you please provide the same (or similar) code fragment in the form that can be actually executed (let’s say in Firebug debugger) and would be useful in exploring this internal machinery? This is a key point to understanding “this” so more details about the Reference type would be extremely useful.
Thanks in advance!
@Luiz
Yes, as you correctly noted, it’s a pseudo-code and just corresponds to the technical algorithm of the specification.
Well, for the
global
we may use directthis
value in the global context as is mentioned in the appropriate section above. I.e.:Alternatively, in the browser environment, we can use
window
binding to refer the global object (though,this
is more correct).Yeah, first,
GetValue
described here is a pseudo-code reflection of the corresponding algorithm of the ES5/ES5 spec. Take a look on it — here (I used ES3 version when was writing this chapter, ES5 version is slightly modified, but the main part is the same).A value of Reference type as mentioned is a pair:
base
andpropertyName
. It can be presented as a constructor in plain JS:When interpreter meets a variable/function name (or in other words — an identifier), it resolves it — i.e. it searches it in the scope chain. This is a process of the identifier resolution which is described in the Chapter 4 Scope chain.
The thing interesting to us, is that the result of the identifier resolution mechanism is always the value of type reference.
If the identifier is found in the scope chain — return the value of
Reference
type with base containing that variable object in which the identifier is found (scope chain as you probably know or will learn in the next chapter 4 is a chain of variable objects from different nested contexts).If the identifier is not found — also the value of Reference type is returned, but with
base
component equals tonull
.And then, if to transform our pseudo-code of
GetValue
more closely to JS, we’ll get:And if we have, e.g. global variable
x
:then after the identifier resolution mechanism, we get the following value of the
Reference
type:Notice, it’s an intermediate result of some calculation, it’s not the real value of
x
yet, i.e.10
. To get exactly the value ofx
, alreadygetValue
is applied:If in contrast we refer a non-existing variable from the code, e.g.
y
, then as we said, we also get the value of Reference type, but withbase
set tonull
:Such intermediate results (of the Reference type) are very important to JS. E.g. exactly via it the work of
typeof
operator is explained. We won’t get an error in the following code:because
typeof
doesn’t call getValue, it just works with the reference itself. However, we do get theReferenceError
if try to just alert they
:and now we know why — because we provided such a behavior in the
getValue
. Functionalert
to display the value of the variable, should callgetValue
:And in the similar respect determination of
this
value for function calls works. If on the left-hand side (LHS) is the value of Reference type, thenthis
is set to the base of referenceGrouping operator, the same as
typeof
also doesn’t call ourgetValue
, which means either with, or without grouping operator — we still have the reference value:However, assignment (the same as
alert
above withy
) does callgetValue
and after its work we already have not the value of Reference type, but the function value, and as a result —this
value is set tonull
and then converted (in ES3) to global object:That is — a shorter, but detailed description of these algorithms. Feel free to ask questions if is needed to clarify something.
Dmitry.
Dmitry,
thanks a lot for a detailed explanation. It’s a lot of food for thought.
Meanwhile this code fragment below runs OK with two exceptions:
1) In function
getValue()
I had to use return v.base instead ofv.base[v.propertyName]
;2) for undefined var y
ReferenceError
is not called in the statements like:or
Thank you.
Luiz.
@Luiz
No, it’s not correct, should be nevertheless
v.base[v.propertyName]
.The mistake you made is that you used not correct
base
component for e.g.x
reference. You usedx
as the base, but it should be the global object. Once again, the base component of the reference is an object to which the property belongs. Global variablex
belongs to global object, therefore global object is its base.You have (wrong):
and it should be:
Also if you defined your own
ReferenceError
constructor (though, it’s a native built-in function), then for tests you may not throw it ingetValue
, but just return. So the complete code to test may look like this:Dmitry.
Dmitry,
thanks a lot for taking time to explain the details. Those are pretty subtle issues that are not discussed anywhere except your articles.
great article! Thanks, learned a lot.
Dmitry,I have test the code in the firefox and chrome.but some thing is not appear as you said.
the resluts is :
could you give me some suggests about this question.thank u
@Shawn
I see correct output in your example. I guess output format of the consoles may confuse, but:
foo.bar();
correctly matches toObject { bar=function()}
— this is thefoo
object.test()
maps to theWindow
which is the global object in the browser environment.fooTwo===fooTwo.prototype.constructor: true
is really true.fooTwo()
also correctly showsWindow
(global).fooTwo {}
maps tofooTwo.prototype
.Hi, Dmitry,
Could you explain please why in IE9 the code in this comment gives f4();//20, but in Firefox f4();//50? What is the reason of such behavior?
Thanks
Please ignore my previous question. I found the answer here
http://dmitrysoshnikov.com/ecmascript/chapter-3-this/comment-page-1/#comment-4859. Sorry.
I Dmitry, i am big fan of yours.
Question: Why there is difference between
x
andy
final output??Example Code is:
@Navneet Goel
This is because of two-dimentional scope-chain look-up. The
x
binding is found inObject.prototype
instead of the global context (since along with thewith
object — the{w: 100}
, its prototype is considered at searching the variable — and this prototype is theObject.prototype
).However, since assignment should occur on the own object, then the
with
object is updated instead of modifying the propertyx
on theObject.prototype
.In contrast with:
More in detail two-dimentional scope-chain look-up (with the picture) is described — here.
thanks for sharing
@frankie TESta
The
this == null -> global
in case of AO relates only to function calls (in order to determine which value thethis
will have at the activation).And the identifier resolution process relates to referencing vars in a code. So, when we call
bar()
above, before it can be activated, thebar
name should be resolved. And its resolution happens infooContext.AO
->global
chain (and is found directly infooContext.AO
).Notice, that
this
value does not participate in identifiers resolution process since it directly belongs to the context. So when we returnthis
frombar
, it means returnbarContext.this
(and it’s set to the global object).Ookz,thank-you.
I was confused by the following codes.
How is the identifier–“foo”(an object) executed by the control?
Does the bar(identifier) in last row of codes participate in identifiers resolution process?
And resuming my last question,My firfox(27.0.1) displays “[object Object]”, not [object foo].
@Drake
Hey Drake,
After I had a quick look of your question, your code would probably cause an error, and it won’t even execute.
Notice the
hope you got it.
Hi Dmitry,
After reading this session, I still have following two questions? Please help. Thanks.
The first one, in following excerption, why similar situation in “catch” object is recognized as a bug?
“The similar situation should be with calling of the function which is the actual parameter of the catch clause: in this case the catch object is also added in front of scope chain i.e. before the activation or global object. However, the given behavior was recognized as a bug of ECMA-262-3 and is fixed in the new version of standard — ECMA-262-5. I.e. this value in the given activation should be set to global object, but not to catch object:”
The second one, could you please give a more detail explanantion of the situation for recursive call of the named functions?
Regards.
Hi Dmitry,
I saw one example on “professional javascript”, while I cannot understand.
Could you please give a description, why we can get each result? Thanks.
In addition, could you please give their “VO”, “AO”, “[[Scope]]” and Scope values?
—————————————
—————————————
Using one intermediate ‘that’, the result is its own name, not the global one, why?
—————————————
—————————————
Please help, waiting for your reply.
Regards.
@Hong
Because simple function calls like `foo()` should set
this
value to global object in ES5. In ES3 it could be the catch-object.As this article explains, the
this
value is not statically bound to a function by default. That is way, when you return a function, and execute it, you havethis
value as the global object (window), since you don’t have a reference value, but function value, when it’s returned (please re-read this chapter, it’s explained there).In the second call, the variable
that
is actually statically captured by closure, and is correctly accessible after the return. And its value is savedthis
.@Dmitry, thanks for your explanation.
I still have some questions.
Q.A, What is the difference b/w following two forms? Context & VO/AO difference?
Could you please help list VO/AO of getNameFunc and that anonymous returned function??
Form 1
Form 2
Q.B Can we take the returned anonymous function as one inner funtion of getNameFunc? Thus, this inner function will be added into the AO of getNameFunc, so ‘this’ will resolved to global.
Q.C in your comment about the second call with “var that=this;”, you said “the variable that is actually statically captured by closure, and is correctly accessible after the return”, statically captured? I think it is still dynamic, “var that=this;” is outside returned anonymous function, so base property of ‘this’ is just ‘object’ and ‘this’ is dynamically resolved to ‘object’, right?
Q.D How to determine the ‘base’ property of one Reference type? Actually, I am not very clear about this “Reference Type” concept either, an abstract concept?
Thanks very much, waitting for your reply:)
Regards,
Hong
@Hong
The Form 1 and Form 2 semantically do not differ: that you saved a function to the
foo
var in Form 2, doesn’t changethis
value at activation, it will be global object (window). Thefoo
identifier is saved though in the second case in the scope chain of the returned function.Activation object (AO) is always created fresh for each function call. So when you call
object.getNameFunc()
:Form 1
Form 2
But when the returned function is called, its
this
value is set to global object.In your previous examples whether
that
was used, that var was captured in closure, and thus had access to thename
property of theobject
.By statically captured, I mean closured. I.e. the returned function has access to
that
, and the value ofthis
on the time whengetNameFunc
is executed, and this value is theobject
.Yes, please re-read this chapter, it explains the reference concept, and its base component. In the simplest case the base is what stands before the dot:
foo.bar()
— the base isfoo
here. Thefoo()
is also reference, with empty base, that is set to global.