JavaScriptECMA-262-3 in-depth analysis. Chapter 3. this_javascript skills

Source: Internet
Author: User
In this article, we will discuss more details directly related to the execution context. The topic of the discussion is the introduction of the this keyword.
In this article, we will discuss more details directly related to the execution context. The topic of the discussion is the this keyword.
Practice has proved that this topic is difficult. It is often difficult to determine the value of this in different execution contexts.
Many programmers tend to think that in programming languages, the this keyword is closely related to object-oriented program development and completely points to the newly created object by the constructor. This is also implemented in the ECMAScript specification, but as we will see, in ECMAScript, this is not limited to pointing only to newly created objects.
Next let's take a closer look at what is the value of this in ECMAScript?
Definition
This is an attribute in the execution context:

The Code is as follows:


ActiveExecutionContext = {
VO :{...},
This: thisValue
};


VO is the variable object discussed in the previous chapter.
This is directly related to the 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 during the context code.
Let's take a closer look at these scenarios.
Value of this in Global Code
Everything is simple here. In the global code, this is always a global object, which may be referenced indirectly.

The Code is as follows:


// Explicit property definition
// 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


Value of this in Function Code
It is interesting to use this in Function Code. this type of application scenario is difficult and can cause many problems.
In this type of code, the primary (or perhaps most important) feature of this value is that it is not statically bound to a function.
As we have mentioned above, the value of this is determined when it enters the context. In function code, the value of this may be completely different once (when the context is entered.
In any case, the value of this remains unchanged during code execution, that is, because this is not a variable, it is impossible to assign a new value to it. (On the contrary, in the Python programming language, it is clearly defined as the object itself, which can be changed continuously during runtime ).

The Code is 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 an error then 20, 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 here this value will now refer
// To "foo"-even though we're re calling the same function
Foo. test (); // false, 10


So, in the function code, what changes affect the value of this? There are several factors.
First, in common function calls, this is provided by the caller who activates the context code, that is, the parent context of the called function (parent context ). This depends on how the function is called. (For more information, see here)
In order to accurately determine this value under any circumstances, it is necessary to understand and remember this important point: it is the way the function is called that affects the value of this in the context of the call, nothing else (we can see in some articles or even in books about javascript that they claim: "the value of this depends on how the function is defined. If it is a global function, this is set as a global object. If a function is an object method, this always points to this object. -This is definitely incorrect "). Continue to our topic. We can see that even normal global functions are activated due to different call methods, which produce different values of this.

The Code is 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 may be called as a method of some objects. At this time, the value of this is not set to this object.

The Code is 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 the method of calling a function affect the value of this? To fully understand how the value of this is determined, we need to analyze in detail an internal type-Reference type ).
Reference Type
The pseudo-code can represent the reference type as the object with two properties-base (that is, the object with the property) and propertyName in the base.

The Code is as follows:


Var valueOfReferenceType = {
Base:,
PropertyName:
};


The value of the reference type only exists in two cases:
1. when we process a identifier; (when we deal with an identifier ;)
2. or an attribute accessor (or with a property accessor .)
The handling process of the identifier is in Chapter 4. scope chain is discussed; here we only need to know that the return value using this processing method is always a value of the reference type (which is important for this ).
The identifier is a variable name, function name, function parameter name, and a property name not recognized in the global object. For example, the following Identifier value:
Var foo = 10;
Function bar (){}
In the intermediate result of the operation, the value of the reference type is as follows:

The Code is as follows:


Var fooReference = {
Base: global,
PropertyName: 'foo'
};
Var barReference = {
Base: global,
PropertyName: 'bar'
};


To obtain the true value of an object from the reference type, you can use the GetValue method (Note: 11.1.6) in pseudo-code:

The Code is 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 values of object attributes, including the analysis of inherited attributes in the prototype chain.
GetValue (fooReference); // 10
GetValue (barReference); // function object "bar"
All attribute accessors should be familiar with this feature. It has two variants: The dot (.) syntax (in this case, the attribute name is a correct identifier and is known in advance), or the bracket syntax ([]).
Foo. bar ();
Foo ['bar'] ();
In the return value in the calculation, the value of the reference type is as follows:

The Code is as follows:


Var fooBarReference = {
Base: foo,
PropertyName: 'bar'
};


GetValue (fooBarReference); // function object "bar"
So, in the most important sense, how is the value of the reference type associated with the value of this in the context of the function? This association process is the core 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 determined by the method of calling the function. If the left side of the brackets () is the reference type value, this is set as the base Object of the reference type value. In other cases (any other attribute different from the reference type ), the value of this is null. However, the actual value of this is null, because when the value of this is null, its value is implicitly converted to a global object.
Here is an example:

The Code is as follows:


Function foo (){
Return this;
}
Foo (); // global


We can see a reference type value on the left side of the call bracket (because foo is a identifier ):

The Code is as follows:


Var fooReference = {
Base: global,
PropertyName: 'foo'
};


Correspondingly, this is also set as a base object of the reference type. Global object.
Similarly, use the property accessors:

The Code is as follows:


Var foo = {
Bar: function (){
Return this;
}
};
Foo. bar (); // foo


Similarly, we have a value of reference type, whose base is the foo object. When the function bar is activated, we set the base to this.

The Code is as follows:


Var fooBarReference = {
Base: foo,
PropertyName: 'bar'
};


However, if you use another method to activate the same function, the value of this will be different.
Var test = foo. bar;
Test (); // global
Because test is used as the identifier to generate values of other reference types, the base (Global Object) of this value is set to the value of this.

The Code is as follows:


Var testReference = {
Base: global,
PropertyName: 'test'
};


Now, we can clearly explain why activating the same function in different forms produces different this values. The answer is the median value of different Reference types.

The Code is 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 typical example of dynamically determining the value of this by calling:

The Code is 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 call and non-reference type
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 actual final value of this is implicitly converted to a global object.
Let's think about the following function expression:

The Code is as follows:


(Function (){
Alert (this); // null => global
})();


In this example, we have a function object, but it is not a reference type object (because it is neither a identifier nor an attribute accessor). Correspondingly, the value of this is eventually set as a global object.
More complex examples:

The Code is 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 an attribute accesser whose median value should be a value of the reference type, but in some calls, the value of this is not a base object, but a global object?
The problem occurs in the following three calls. After a certain operation is performed, the value on the left of the call bracket is no longer of the reference type.
The first example is obvious-the obvious reference type. The result is that this is the base object, that is, foo.
In the second example, the grouping operator refers to foo. the parentheses "()") outside the bar have no practical significance. Think about the method mentioned above to get the true value of an object from the reference type, such as GetValue (see 11.1.6 ). Correspondingly, in the return value of the group operation, what we get is still a reference type. This is why the value of this is set as the base object again, that is, foo.
In the third example, unlike the grouping operator, the value assignment operator calls the GetValue method (refer to step 3 of 11.13.1 ). The returned result is already a function object (not a reference type). this means that the value of this is set to null, and the actual final result is set to a global object.
The fourth and fifth are the same-the comma operator and the logical operator (OR) Call the GetValue method. Correspondingly, we lose the value of the reference type and get the value of the function type, therefore, the value of this is set as a global object again.
The reference type and this are null.
If the call method determines the value of the Reference type (when call expression determinates on the left hand side of call brackets the value of Reference type. The Translator's note: the original article is a little slow !), In any case, as long as the value of this is set to null, it will be implicitly converted to global. This situation occurs when the base object of the referenced type value is an activated object.
In the following example, the internal function is called by the parent function, so we can see the special situation mentioned above. As we learned in Chapter 2, local variables, internal functions, and form parameters are stored in the activation object of the given function.

The Code is as follows:


Function foo (){
Function bar (){
Alert (this); // global
}
Bar (); // the same as AO. bar ()
}


The activation object is always returned as the value of this -- null (that is, the pseudocode AO. bar () is equivalent to null. bar ()). (Note: Do not understand here) Here we return to the situation described above again, and the value of this is eventually set as a global object.
Except for one case: "When a function is called in the with statement and contains the function name attribute in the with object (Note: _ withObject in the following example ". The with statement adds its object to the frontend of the scope chain, that is, before the activated object. Then, the reference type has a value (through the identifier or attribute accessors), and its base object is no longer an activation object, but an object of the with statement. By the way, this situation is not only related to internal functions, but also to global functions, because with objects are more advanced than the objects (global objects or an activated object) in the scope chain) it must be on the front.

The Code is as follows:


Var x = 10;
With ({
Foo: function (){
Alert (this. x );
},
X: 20
}){
Foo (); // 20
}
// Because
Var fooReference = {
Base: _ withObject,
PropertyName: 'foo'
};


Function calls in the actual parameters of a catch statement are similar: In this case, the catch object is added to the frontend of the scope, that is, before the active object or global object. However, this particular behavior was identified as a bug in the ECMA-262-3, which was fixed in the new ECMA-262-5. After the fix, in a specific activation object, this points to the global object. Instead of catch objects.

The Code is 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 this is a bug
// Then this value is forced to global
// Null => global
Var eReference = {
Base: global,
PropertyName: 'E'
};


The same situation occurs in the recursive call of the name function (for more information about the function, see Chapter 5. Functions. In the first call of a function, the base object is a parent activation object (or a global object). In recursive calls, the base object should be a specific object that stores the optional name of a function expression. However, in this case, the value of this is always set to global.

The Code is as follows:


(Function foo (bar ){
Alert (this );
! Bar & foo (1); // "shocould" be special object, but always (correct) global
}) (); // Global


Value of this in the function called as the constructor
In the context of a function, another condition related to the value of this is that the function is called as a constructor.

The Code is as follows:


Function (){
Alert (this); // newly created object, below-"a" object
This. x = 10;
}
Var a = new ();
Alert (a. x); // 10


In this example, the new operator calls the [[Construct] method inside the "A" function, and then calls the [[Call] method inside the function after the object is created, all the same function "A" sets the value of this as the newly created object.
Manually set this for a function call
Function. prototype defines two methods that allow you to manually set the value of this when a Function is called. They are. apply and. call methods (all functions can access them ). They use the first accepted parameter as the value of this, which is used in the scope of the call. The two methods have little difference. For. apply, the second parameter must be an array (or an object similar to an array, such as arguments. On the contrary,. call can accept any parameter. The parameters required by both methods are the first -- this.
For example

The Code is as follows:


Var B = 10;
Function a (c ){
Alert (this. B );
Alert (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 discuss the features (and they really are features, in contrast, say, with C ++ or Java) of the this keyword in ECMAScript. Note: this sentence is useless, but I still don't know what to do ). I hope this article will help you understand exactly how the this keyword works in ECMAScript. Similarly, I am very 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 CILS.
ECMA-262-3 in detail. Chapter 3. This.
[JavaScript] ECMA-262-3 in-depth analysis. Chapter 3. this
Translation statement:
1. since Denis has already translated this article, this article references his translation in some chapters, and the reference part accounts for about 30% of the entire article, another 70% or so is completely re-translated.
2. During the translation process, I have fully communicated with the original author. when reading the translation, you can refer to the original text message list.
3. Even a good translation cannot keep up with the original text. Therefore, we recommend that you read the original text carefully after reading the translation.
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.