JavaScript ecma-262-3 in-depth parsing. Chapter III. THIS_JAVASCRIPT Skills

Source: Internet
Author: User
Introduced
In this article, we will discuss more details that are directly related to the execution context. The subject of the discussion is the this keyword.
Practice has shown that this topic is difficult, and it is often problematic to determine the value of this in different execution contexts.
Many programmers are accustomed to the view that in the programming language, the This keyword is closely related to Object-oriented program development, and it completely points to the newly created object by the constructor. This is done in the ECMAScript specification, but as we'll see, in ECMAScript, this is not limited to pointing to newly created objects.
Let's take a closer look at the value of this in ECMAScript.
Defined
This is an attribute in the execution context:
Copy Code code as follows:

Activeexecutioncontext = {
VO: {...},
This:thisvalue
};

Here Vo is the variable object we discussed in the previous chapter.
This is directly related to executable code (type) in the context. The value of this is determined when the context is entered, and the value of this is not changed while the context is running code.
Let's look at these scenarios in more detail.
This is the value in the global code
Everything is simple here. In global code, this is always the global object itself, so it is possible to refer to it indirectly.
Copy Code code as follows:

Explicit property Definition of
The Global object
THIS.A = 10; GLOBAL.A = 10
alert (a); 10
implicit definition via assigning
to unqualified identifier
b = 20;
alert (this.b); 20
Also implicit via variable declaration
Because variable object of the global context
is the global object itself
var C = 30;
alert (THIS.C); 30

This is the value in the function code
It is interesting to use this in the function code, which is difficult and can cause a lot of problems.
In this type of code, the first (and perhaps most important) feature of this value is that it is not statically bound to a function.
As we mentioned above, the value of this is determined when it enters the context, and in the function code, the value of this is likely to be completely different each time (when entering the context).
In any case, the value of this is constant during the code run, that is, because this is not a variable, it is not possible to assign a new value to it. (instead, in the Python programming language, it is explicitly defined as the object itself and can be changed during runtime).
Copy Code code as follows:

var foo = {X:10};
var bar = {
X:20,
Test:function () {
Alert (This = = bar); True
alert (this.x); 20
this = foo; Error
alert (this.x); If there wasn ' t a error then, not 10
}
};
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 this value would now refer
To "foo" –even though we ' re calling the same function
Foo.test (); False, 10

So, in the function code, what affects the value of this change? There are several factors.
First, in the usual function call, this is provided by the caller who activated the context code, that is, the parent context of the calling function. This depends on how the function is invoked. (Translator Note: refer here)
In order to accurately determine the this value in any case, it is necessary to understand and remember this important point: it is the way the function is invoked that affects the value of this in the context of the invocation, nothing else (we can see in some articles, even in books about JavaScript, that they claim: " The value of this depends on how the function is defined, and if it is a global function, this is set as a global object, and if the function is a method of an object, this will always point to the object. – This is absolutely incorrect "). Continuing with our topic, we can see that even normal global functions are activated by different invocation methods, which produce this different value.
Copy Code code as follows:

function foo () {
alert (this);
}
Foo (); Global
Alert (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

Sometimes a function can be invoked as a method of some object, at which point the value of this is not set to this object.
Copy Code code as follows:

var foo = {
Bar:function () {
alert (this);
Alert (This = = = foo);
}
};
Foo.bar (); Foo, True
var examplefunc = Foo.bar;
Alert (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 calling a function affect the value of this? To fully understand how the value of this is determined, we need to analyze an internal type (internal type) in detail-the reference type (Reference type).
Reference type
A reference type can be represented by Pseudocode as an object--base with two attributes (that is, the object that owns the attribute), and the PropertyName in base.
Copy Code code as follows:

var Valueofreferencetype = {
Base: <base Object>
PropertyName: <property name>
};

The value of a reference type exists only in two cases:
1. When we are dealing with a designator (when we are deal with a identifier;)
2. or an attribute accessor; (or with a property accessor.)
The processing of the designator is in Chapter 4. Scope chain; Here we just need to know that the return value using this process is always a reference type value (which is important for this).
Identifiers are variable names, function names, function parameter names, and unrecognized property names in global objects. For example, the value of the following identifier:
var foo = 10;
function bar () {}
In the intermediate results of the operation, the reference type corresponds to the following values:
Copy Code code as follows:

var fooreference = {
Base:global,
PropertyName: ' foo '
};
var barreference = {
Base:global,
PropertyName: ' Bar '
};

In order to get the real value of an object from a reference type, you can use the GetValue method (translator note: 11.1.6) to represent it in Pseudocode, as follows:
Copy Code code 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));
}

The internal [[get]] method returns the true value of the object property, including an analysis of the inherited property in the prototype chain.
GetValue (fooreference); 10
GetValue (barreference); Function Object "Bar"
Property accessors should be familiar. It has two variants: the point (.) syntax (at which time the property name is the correct identifier and is known beforehand), or the parenthesis syntax ([]).
Foo.bar ();
foo[' Bar '] ();
In the calculation of the return value in the middle, the reference type corresponds to the following values:
Copy Code code as follows:

var foobarreference = {
Base:foo,
PropertyName: ' Bar '
};

GetValue (foobarreference); Function Object "Bar"
So, in the most important sense, how does the value of a reference type relate to the value of this in the context of a function? The associated process is at the heart of this article. (The given moment is the main of this article.) The general rules for determining the value of this in a function context are as follows:
In a function context, the value of this is provided by the caller and is determined by the way the function is invoked. If the left side of the call bracket () is the value of the reference type, this is set to the base object for this reference type value, and in other cases (any other property that differs from the reference type), this value is null. However, there is no actual case where this value is NULL, because its value is implicitly converted to the global object when the value of this is null.
Let's take a look at the following example:
Copy Code code as follows:

function foo () {
return this;
}
Foo (); Global

We see a reference type value on the left side of the call bracket (because Foo is an identifier):
Copy Code code as follows:

var fooreference = {
Base:global,
PropertyName: ' foo '
};

Accordingly, this is also set to the base object of the reference type. is the global object.
Also, use the property accessor:
Copy Code code as follows:

var foo = {
Bar:function () {
return this;
}
};
Foo.bar (); Foo

Similarly, we have a value of a reference type whose base is the Foo object and set the base to this when function bar is activated.
Copy Code code as follows:

var foobarreference = {
Base:foo,
PropertyName: ' Bar '
};

However, if you activate the same function in another way, the value of this will be different.
var test = Foo.bar;
Test (); Global
Because test, as an identifier, produces a value of another reference type, the base (global object) of that value is set to the value of this.
Copy Code code as follows:

var testreference = {
Base:global,
PropertyName: ' Test '
};

Now, we can clearly explain why activating the same function in a different form produces a different this, and the answer lies in the median of different reference types (type Reference).
Copy Code code as follows:

function foo () {
alert (this);
}
Foo (); Global, because
var fooreference = {
Base:global,
PropertyName: ' foo '
};
Alert (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 classic example of dynamically determining the value of this by calling the method:
Copy Code code as follows:

function foo () {
alert (This.bar);
}
var x = {Bar:10};
var y = {bar:20};
X.test = foo;
Y.test = foo;
X.test (); 10
Y.test (); 20

function calls and non-reference types
So, as we have already pointed out, when the left side of the call bracket is not a reference type but another type, the value of this is automatically set to NULL, and the value of the actual final this is implicitly converted to the global object.
Let's think about the following expression of a function:
Copy Code code as follows:

(function () {
alert (this); Null => Global
})();

In this example, we have a function object that is not a reference type (because it is not an identifier or a property accessor), and accordingly, the value of this is eventually set to the global object.
More complex examples:
Copy Code code as follows:

var foo = {
Bar:function () {
alert (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 do we have a property accessor whose intermediate value should be the value of a reference type, but in some calls we get the value of this is not a base object, but a global object?
The problem appears in the following three calls, and after performing a certain operation, the value on the left side of the call bracket is no longer a reference type.
The first example is obviously ——— obvious reference type, and as a result, this is the base object, that is, Foo.
In the second example, the group operator (translator Note: The grouping operator here refers to the foo.bar outer bracket "()") without practical meaning, think of the above mentioned method of obtaining an object's true value from a reference type, such as GetValue (reference 11.1.6). Accordingly, in the return value of the grouping operation ——— What we get is still a reference type. This is why the value of this is again set to the base object, that is, Foo.
In the third example, unlike the grouping operators, the assignment operator invokes the GetValue method (refer to the third step of the 11.13.1). The result returned is already a function object (not a reference type), which means that the value of this is set to NULL, and the actual end result is set to the global object.
The fourth and fifth are the same--the comma operator and the logical operator (or) call the GetValue method, and correspondingly, we lose the value of the reference type and get the value of the function type, so the value of this is again set to the global object.
Reference type and this is null
In one case, if the invocation method determines the value of the reference type (when called Expression determinates on the left hand side of call brackets the value of Reference type 。 The translator notes that the original text is a bit dilatory! Anyway, as long as the value of this is set to NULL, it will eventually be implicitly converted to global. This can occur when a base object that refers to a type value is an active object.
In the following example, the intrinsic function is called by the parent function, and we can see the special case described above. As we learned in chapter two, local variables, internal functions, and formal parameters are stored in the activation object of the given function.
Copy Code code as follows:

function foo () {
function Bar () {
alert (this); Global
}
Bar (); The same as Ao.bar ()
}

The active object always returns--null as this value (that is, the pseudo code Ao.bar () is the equivalent of Null.bar ()). (Translator Note: Do not understand the reference here) here we go back to the case described above, the value of this is ultimately set to the global object.
In one case, the function is called in the With statement, and the function Name property is included in the With object (the __withobject in the example below). The WITH statement adds its object to the front of the scope chain, that is, before the active object. Accordingly, a reference type has a value (through an identifier or a property accessor) whose base object is no longer an active object, but an object of the WITH statement. By the way, this is not only related to internal functions, but also to global functions, because the with object is ahead of the most front-end object in the scope chain (global object or an active object).
Copy Code code as follows:

var x = 10;
With ({
Foo:function () {
alert (this.x);
},
X:20
}) {
Foo (); 20
}
Because
var fooreference = {
Base: __withobject,
PropertyName: ' foo '
};

A similar case exists in a function call in the actual argument of a catch statement: In this case, the catch object is added to the front of the scope, that is, before activating the object or the global object. However, this particular behavior is believed to be a bug in Ecma-262-3, which is fixed in the new version of Ecma-262-5. After the fix, in a specific activation object, this points to the global object. Rather than a Catch object.
Copy Code code as follows:

try {
Throw function () {
alert (this);
};
catch (e) {
E (); __catchobject-in ES3, global-fixed in ES5
}
On idea
var ereference = {
Base: __catchobject,
PropertyName: ' E '
};
But, as is a bug
Then this value are forced to global
Null => Global
var ereference = {
Base:global,
PropertyName: ' E '
};

The same situation appears in the name function (function's more detail Reference Chapter 5.) Functions) in a recursive call. In the first invocation of a function, the base object is a parent-activated object (or global object), and in a recursive invocation, the base object should be a specific object that stores the optional name of the function expression. In this case, however, the value of this is always set to global.
Copy Code code as follows:

(function foo (bar) {
alert (this);
!bar && foo (1); "Should" be special object, but always (correct) global
})(); Global

This is the value in the function that is called by the constructor
There is also a situation in the context of a function that is related to the value of this: When a function is invoked as a constructor.
Copy Code code as follows:

function A () {
alert (this); Newly created object, below-"a" object
This.x = 10;
}
var a = new A ();
alert (a.x); 10

In this example, the new operator invokes the [[construct]] method inside the "a" function, and then, after the object is created, calls its internal [[call]] method, and all the same function "A" sets the value of this to the newly created object.
Manually set this for a function call
There are two methods defined in Function.prototype that allow you to manually set the value of this when the function is called, which are. Apply and. Call methods (all functions can access them). They use the first argument accepted as the value of this, which is used in the scope of the invocation. There is little difference between the two methods, for. Apply, the second argument must be an array (or an array-like object, such as arguments, instead,. Call can accept any argument. All two methods must be the first--this.
For example
Copy Code code as follows:

var B = 10;
function A (c) {
alert (this.b);
alert (c);
}
A (20); this = = Global, this.b = = 20
A.call ({b:20}, 30); this = = = {B:20}, this.b = = = 30
A.apply ({b:30}, [)]//This = = {B:30}, this.b = = = 40

Conclusion
In this article, we discussed the characteristics of this keyword in ECMAScript (and they really are features, in contrast, say, with C + + or Java), the translator Note: This sentence is not very good, do not know how to turn, temporarily Don't turn it over). I hope this article will help you to understand exactly how the This keyword works in ECMAScript. Again, I'm happy to answer your question in the comments.
Other references
10.1.7–this;
11.1.1–the this keyword;
11.2.2–the new operator;
11.2.3–function calls.
English address: ecma-262-3 in detail. Chapter 3. this.
Chinese address: [javascript]ecma-262-3 in-depth analysis. Chapter III. This
Translation statement:
1. Since Denis has already translated this article, the translation is referred to in some chapters as referring to about 30% of the entire article, and the other 70% or so is completely translatable.
2. In the process of translation, with the original author of the full communication, we look at the translation, you can refer to the original message list.
3. A good translation can not catch up with the original text, so we recommend that you read the translation after the original text or look carefully.
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.