Home  >  Article  >  Web Front-end  >  In-depth understanding of JavaScript series (13) This? Yes, this!_javascript skills

In-depth understanding of JavaScript series (13) This? Yes, this!_javascript skills

WBOY
WBOYOriginal
2016-05-16 17:56:53843browse

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 the determination 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.

English translation: Dmitry A. Soshnikov with help from Stoyan Stefanov
Published: 2010-03-07
http://dmitrysoshnikov.com/ecmascript/chapter-3-this/

Original Russian text: Dmitry A. Soshnikov
Correction: Zerogif
Released: 2009-06-28;
Updated: 2010-03-07
http://dmitrysoshnikov.com/ ecmascript/ru-chapter-3-this/

Most of the content of this article refers to: http://www.denisdeng.com/?p=900
Some sentences refer to: justin’s Chinese translation
Copy code
Let us take a closer look at what exactly is this in ECMAScript?

Definition
this is an attribute in the execution context:

Copy code The code is as follows:

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

The VO here is what we discussed in the previous chapter variable object.

This is directly related to the type of executable code in the context. The value of this is determined when entering the context and remains unchanged while the context is running.

Let's study these cases in more detail:

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:

// Display the attributes that define the global object
this .a = 10; // global.a = 10
alert(a); // 10

// Implicit by assigning to an unidentified
b = 20;
alert(this.b); // 20

// is also implicitly declared through variable declaration
// because the variable object of the global context is the global object itself
var c = 30;
alert(this.c); // 30

this in function code
It is interesting when using this in function code, this situation is difficult and can cause a lot of problems.

The first (perhaps the most important) feature of this value in this type of code is that it is not statically bound to a function.

As we mentioned above, this is determined when entering the context. In a function code, this value is completely different every time.

In any case, the value of this is unchanged while the code is running, that is, since it is not a variable, it is not possible to assign a new value to it (in contrast, in the Python programming language, it Well-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, the value of this cannot be changed at any time

alert(this.x); // If there is no error, it should be 10, and Not 20

}

};

// When entering the context
// this is regarded as the bar object
// determined as "bar" object; why so - will
// be discussed below in detail

bar.test(); // true, 20

foo.test = bar.test;

// However, here this still will not be foo
// Although the same function is called

foo.test(); // false, 10

So, there are several factors that affect the change of this value in the function code:

First of all, in a normal function call, this is provided by the caller who activates the context code, that is, calling the function 's parent context. this depends on how the function is called.

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 the function is called that affects the this value in the context of the call, nothing else (we can see in some articles, even in books about javascript, they claim: "the this value depends on how the function is defined, If it's a global function, this is set to the global object, if the function is a 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 by different forms of calling methods. These different calling methods lead to different this values.
Copy code The code is as follows:

function foo() {
alert(this) ;
}

foo(); // global

alert(foo === foo.prototype.constructor); // true

// But the same For different calling expressions of a function, this is different

foo.prototype.constructor(); // foo.prototype

may be used as a method defined by some objects The function is called, but 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

// Once again, different calling expressions for the same function , this is different

exampleFunc(); // global, false

So, how does the way of calling the function affect the this value? In order to fully understand the determination of this value, one of its internal types - reference type (Reference type) needs to be analyzed in detail.

Reference type (Reference type)
Using pseudocode, we can represent the value of the reference type as an object with two properties - base (that is, the object with the properties), and base in propertyName.
Copy code The code is as follows:

var valueOfReferenceType = {
base: ,
propertyName:
};

There are only two situations for reference type values:

When we deal with an identifier or a property accessor
identifier will be discussed in detail in the next article. Here we only need to know that the return value of this algorithm is always a reference type value (this is different for this It’s important to say).

Identifiers are variable names, function names, function parameter names and unrecognized property names in global objects. For example, the value of the following identifier:

Copy the code The code is as follows:
var foo = 10 ;
function bar() {}

In the intermediate result of the operation, the value corresponding to 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 value of an object from the reference type, in the pseudo code The GetValue method can be described 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.

Copy code The code is as follows:

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 ([]).
Copy code The code is as follows:

foo.bar();
foo[' bar']();

In the return value of the intermediate calculation, we have the value of the reference type.
Copy code The code is as follows:

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

GetValue(fooBarReference); // function object "bar"

The value of the reference type and the this value in the function context How is it relevant? — in the most important sense. This process of association is at the heart of this article. The general rules for determining the value of this in a function context are as follows:

In a function context, 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 value of reference type, this will be set to the base object of the reference type value. In other cases (any other properties different from the reference type), this value 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. Note: In the fifth edition of ECMAScript, conversion to global variables is no longer forced, but is assigned to undefined.

Let’s take a look at the performance in this example:
Copy the code The code is as follows:

function foo() {
return this;
}

foo(); // global

We see that to the left of the calling bracket is a Reference type value (because foo is an 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

We again have a reference type whose base is the foo object, which is used as this when function bar is activated.
Copy code The code is as follows:

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

However, activating the same function in another form, we get other this values.
Copy code The code is as follows:

var test = foo.bar;
test( ); // global

Because test is used as an identifier, other values ​​of the reference type are generated, and its base (global object) is used as this value.
Copy code The code is as follows:

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

Now, we can clearly tell you why activating the same function with different forms of expressions will have different this values. The answer is Intermediate values ​​with different reference types (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 one A form of calling 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(); // 10
y.test(); // 20

Function calls and non-reference types
So, as we have pointed out, when the left side of the calling bracket is not a reference type but other types , this value is automatically set to null, and the result is a global object.

Let us think about this expression again:
Copy the code The code is as follows:

(function () {
alert(this); // null => global
})();

In this example, we have a function object But it is not an object of reference type (it is not an identifier or a property accessor). Accordingly, the this value 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?

Why do we have a property accessor in the middle of it The value should be a reference type value. In some calls, the this value we get is not the base object, but the global object?

The problem is that in the following three calls, after applying 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 group operator does not apply. Think of the methods mentioned above that obtain the actual value of an object from a reference type, such as GetValue. Correspondingly, in the return of the group operation - we still get a reference type. This is why the this value is set to the base object again, which is foo.
In the third example, unlike the group operator, the assignment operator calls the GetValue method. The returned result is a function object (but not a reference type), which means this is set to null and the result is a global object.
The same goes for the fourth and fifth ones - the comma operator and the logical operator (OR) call the GetValue method, and accordingly, we lose the reference and get the function. And set it to global again.
Reference type and this is null
There is a situation like this: when the calling expression limits the value of the reference type on the left side of the call bracket, although this is set to null, the result is converted to global. This situation occurs when the base object of a reference type value is the active 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 12, 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()
}

The active object is always returned as this, and the value is null - (that is, AO.bar() in pseudo code is equivalent to null.bar()). Here we return to the example described above again, with this set to the global object.

There is one exception: if the with object contains a function name attribute, the function is called in the inner block of the with statement. The With statement is added to the front of the object's scope, that is, in front of the active object. Correspondingly, there are reference types (through identifiers or property accessors) whose base object is no longer the active object, but the object of the with statement. By the way, it is not only relevant 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 active 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'
};

The same situation occurs in the actual parameters of the catch statement function call: in this case, the catch object is added to the front of the scope, that is, in the active object or global object Front. However, this specific behavior was confirmed to be a bug in ECMA-262-3, which is fixed in the new version of ECMA-262-5. In this way, in a specific active 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(); // ES3 standard is __catchObject, ES5 standard is global
}

// on idea

var eReference = {
base: __catchObject,
propertyName: 'e'
};

// This has been fixed in the new ES5 standard This bug,
// So this is the global object
var eReference = {
base: global,
propertyName: 'e'
};

The same situation occurs with recursive calls to named functions (see Chapter 15 Functions for more details on functions). In the first call of the function, the base object is the parent activity 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, this always points to the global object.
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 in a function called as a constructor
Another situation related to this value is in the context of a function, which is a call to a constructor.
Copy code The code is as follows:

function A() {
alert(this) ; // Create a new attribute under the "a" object
this.x = 10;
}

var a = new A();
alert(a.x); // 10

In this example, the new operator calls the internal [[Construct]] method of the "A" function, and then, after the object is created, calls the internal [[Call]] method. All the same function "A" sets the value of this to the newly created object.

Manually setting this in function calls
Two methods defined in the function prototype (so all functions can access it) allow to manually set the this value in function calls. They are .apply and .call methods. They use the first argument they accept as the this value, and this is used in the calling scope. The difference between these two methods is very small. For .apply, the second parameter must be an array (or an array-like object, such as arguments. In turn, .call can accept any parameter. The required parameter for both methods is the first ——this.

For example:
Copy code 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 discussed the characteristics of this keyword in ECMAScript (compared to C and Java, they are indeed characteristics). I hope this article helped you understand exactly how the this keyword works in ECMAScript. Again, I'd be happy to get back to your question in the comments.

Other references
Statement:
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