Home > Web Front-end > JS Tutorial > body text

JavaScript ECMA-262-3 in-depth analysis. Chapter 3. this_javascript skills

WBOY
Release: 2016-05-16 18:01:37
Original
791 people have browsed it

Introduction
In this article, we will discuss more details directly related to execution context. The topic of discussion is this keyword.
Practice has proven that this topic is difficult, and problems often occur in determining the value of this in different execution contexts.
Many programmers are accustomed to believing that in programming languages, the this keyword is closely related to object-oriented program development, and it completely points to the object newly created by the constructor. This is also implemented in the ECMAScript specification, but as we will see, in ECMAScript, this is not limited to only pointing to newly created objects.
Let’s take a closer look at what is the value of this in ECMAScript?
Definition
this is an attribute in the execution context:

Copy code The code is as follows:

activeExecutionContext = {
VO: {...},
this: thisValue
};

Here VO is the variable object we discussed in the previous chapter.
This is directly related to (the type of) executable code in the context. The value of this is determined when entering the context and does not change while the context is running code.
Let’s examine these scenarios in more detail.
The value of this in 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 The code is 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

The value of this in function code
It is interesting to use this in function code, this kind of Application scenarios are difficult and cause many problems.
The first (perhaps the most important) characteristic of the this value in this type of code is that it is not statically bound to a function.
As we mentioned above, the value of this is determined when entering the context. In function code, the value of this may be completely different each time (when entering the context).
No matter what, the value of this is unchanged while the code is running, that is, because this is not a variable, it is impossible to assign a new value to it. (In contrast, in the Python programming language, it is explicitly defined as the object itself, which can be continuously changed during runtime).
Copy code 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 calling the same function
foo.test(); // false, 10

So, in the function code, what affects the value of this to change? There are several factors.
First of all, in a normal function call, this is provided by the caller who activates the context code, that is, the parent context of the calling function. this depends on how the function is called. (Translator's Note: Refer here)
In order to accurately determine the value of this in any situation, it is necessary to understand and remember this important point: it is the way in which the function is called that affects the value of this in the context of the call. Nothing else (we can see in some articles, even in books about javascript, they claim: "The value of this depends on how the function is defined, if it is a global function, this is set to the global object, if the function is method of an object, this will always point to the object - this is absolutely not true"). Continuing our topic, we can see that even normal global functions will be activated due to different calling methods, and these different calling methods produce different values ​​of this.
Copy code 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 object, and the value of this will not be set to this object.
Copy code 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 way of calling the function affect the value of this? In order to fully understand how the value of this is determined, we need to analyze in detail an internal type (reference type).
Reference type
Using pseudo code, the reference type can be represented as an object with two properties - base (that is, the object with the property), and the propertyName in base.
Copy code The code is as follows:

var valueOfReferenceType = {
base: ,
propertyName:
};

Reference type values ​​only exist in two situations:
1. When we deal with an identifier When;(when we deal with an identifier;)
2. Or with a property accessor;(or with a property accessor.) The processing of
identifier is discussed in Chapter 4. Scope chain; here we Just know that the return value using this method is always a reference type value (this 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 result of the operation, the corresponding value of the reference type is as follows:
Copy code The code is as follows:

var fooReference = {
base: global,
propertyName: 'foo'
};
var barReference = {
base: global,
propertyName: 'bar'
};

In order to get the real object of an object from a reference type Value can be represented by the GetValue method (Translator's Note: 11.1.6) in pseudo code, as follows:
Copy 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 real value of the object's properties, including analysis of inherited properties in the prototype chain.
GetValue(fooReference); // 10
GetValue(barReference); // function object "bar"
Attribute accessors should all be familiar. It has two variants: dot (.) syntax (when the property name is the correct identifier and is known in advance), or bracket syntax ([]).
foo.bar();
foo['bar']();
In the calculation of the return value in the middle, the corresponding value of the reference type is as follows:
Copy code The code is as follows:

var fooBarReference = {
base: foo,
propertyName: 'bar'
} ;

GetValue(fooBarReference); // function object "bar"
So, in the most important sense, how are the values ​​of reference types related to the value of this in the function context? This process of association 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 called. . If the left side of the calling bracket () is a reference type value, this will be set to the base object of the reference type value. In other cases (any other properties different from the reference type), the value of this will be null. However, there is no actual situation where the value of this is null, because when the value of this is null, its value will be implicitly converted to the global object.
Let’s look at an example:
Copy the code The code is as follows:

function foo () {
return this;
}
foo(); // global

We see that on the left side of the calling bracket is a reference type value (because foo is a identifier):
Copy code The code is as follows:

var fooReference = {
base : global,
propertyName: 'foo'
};

Correspondingly, this is also set to the base object of the reference type. That is the global object.
Similarly, use the property accessor:
Copy the code The code is as follows:

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

Similarly, we Have a reference type value whose base is the foo object. When the function bar is activated, the base is set to this.
Copy code The code is as follows:

var fooBarReference = {
base: foo,
propertyName: 'bar'
};

However, if the same function is activated in another way, the value of this will be different.
var test = foo.bar;
test(); // global
Because test is used as an identifier, it generates other reference type values, and the base (global object) of this value is set to this. value.
Copy code The code is as follows:

var testReference = {
base: global,
propertyName: 'test'
};

Now, we can clearly explain why activating the same function in different forms will produce different this. The answer lies in different references. The intermediate value of the type (type Reference).
Copy code 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 classic example of dynamically determining the value of this by calling:
Copy code 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( ; But for other types, the value of this is automatically set to null, and the value of this is actually implicitly converted to the global object.
Let us think about the following function expression:



Copy code
The code is as follows: (function () { alert(this); // null => global })();

In this example, we have a function object but not a reference type object (because it is not an identifier or a property accessor). Accordingly, the value of this is ultimately set to the global object.
More complex examples:
Copy the code 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 a property accessor whose intermediate value should be a reference type value? , but in some calls we get that the value of this is not the base object, but the global object?
The problem occurs in the next three calls. After performing certain operations, the value on the left side of the calling bracket is no longer a reference type.
The first example is obvious - an obvious reference type. The result is that this is the base object, which is foo.
In the second example, the grouping operator (Translator's Note: The grouping operator here refers to the brackets "()" outside foo.bar) has no practical meaning. Think about the above mentioned, from the reference type Methods to obtain the real value of an object, such as GetValue (refer to 11.1.6). Correspondingly, in the return value of the grouping operation - what we get is still a reference type. This is why the value of this is set to the base object again, which is foo.
In the third example, unlike the grouping operator, the assignment operator calls the GetValue method (refer to the third step of 11.13.1). The returned result is already a function object (not a reference type), which 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 ones are the same - the comma operator and logical operator (OR) call the GetValue method. Correspondingly, we lose the reference type value and get the function type value, so this The value is again set to the global object.
Reference type and this is null
There is a situation when call expression determines on the left hand side of call brackets the value of Reference type. Translator's note, the original text is a bit Delay!), no matter what, as long as the value of this is set to null, it will eventually be implicitly converted to global. This situation occurs when the base object of a reference type value is the activation object.
In the following example, the internal function is called by the parent function. At this time, we can see the special situation mentioned above. As we learned in Chapter 2, local variables, internal functions, and formal parameters are stored in the activation object of a given function.
Copy code The code is as follows:

function foo() {
function bar() {
alert(this); // global
}
bar(); // the same as AO.bar()
}

Activated object is always Returns - null as the value of this (that is, pseudocode AO.bar() is equivalent to null.bar()). (Translator's Note: If you don't understand, refer here) Here we return to the situation described above again, and the value of this is eventually set to the global object.
There is an exception: "When a function is called in a with statement, and the function name attribute is included in the with object (Translator's Note: __withObject in the example below)". The with statement adds its object at the front of the scope chain, that is, before the activation object. Correspondingly, the reference type has a value (through an identifier or a property accessor), and its base object is no longer the activation object, but the object of the with statement. By the way, this situation is not only related to internal functions, but also to global functions, because the with object is earlier than the frontmost object in the scope chain (the global object or an activation object).
Copy code 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 in the actual parameters of the catch statement A similar situation exists for calls: in this case, the catch object is added to the front of the scope, that is, in front of the activation object or the global object. However, this specific behavior was confirmed to be a bug in ECMA-262-3, which was fixed in the new version of ECMA-262-5. After the fix, in the specific activation object, this points to the global object. rather than the catch object.
Copy code 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 recursive calls to named functions (see Chapter 5. Functions for more details on functions). In the first call of the function, the base object is the parent activation object (or global object). In recursive calls, the base object should be a specific object that stores the optional name of the function expression. However, in this case, the value of this is always set to global.
Copy code The code is as follows:

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

this Values ​​in functions called as constructors
Another situation where the value of this is relevant in the context of a function is when the function is called as a constructor.
Copy code The code is 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 calls the [[Construct]] method inside the "A" function, and then, after the object is created, calls its internal [[Call]] method, all the same Function "A" both sets the value of this to the newly created object.
Manually set the this of a function call
Two methods are defined in Function.prototype to allow manual setting of the value of this when the function is called. They are the .apply and .call methods (all functions can access them) . They use the first argument they accept as the value of this, which is used in the calling scope. There is not much difference between the two methods. For .apply, the second parameter must be an array (or an array-like object, such as arguments. On the contrary, .call can accept any parameters. The required parameters of both methods are the first ——this. 10;
function a(c) {
alert(this.b); Conclusion
In this article, we discuss the characteristics of this keyword in ECMAScript (and they really are features, in contrast, say, with C or Java, Translator’s Note: This sentence is not very useful, I don’t know yet) OK, I won’t read it for now). I hope this article helps you understand exactly how this keyword works in ECMAScript. Likewise, I’m happy to answer your questions 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 3.this
Translation statement:
1. Because Denis has already translated this article, so This translation refers to his translation in some chapters, and the reference part accounts for about 30% of the entire article, and the other 70% is completely re-translated
2. During the translation process, I consulted with the original author. Full communication. When you read the translation, you can refer to the original message list.
3. No matter how good the translation is, it cannot match the original text, so I recommend that you read the original text carefully after reading the translation.
Related labels:
source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
About us Disclaimer Sitemap
php.cn:Public welfare online PHP training,Help PHP learners grow quickly!