In-depth understanding of the JavaScript series (13): This? Yes, this !, Understanding javascript

Source: Internet
Author: User

In-depth understanding of the JavaScript series (13): This? Yes, this !, Understanding javascript
Introduction

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 very difficult, and this often occurs 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.

Dmitry A. Soshnikov was released with the help of Stoyan Stefanov: 2010-03-07http: // adjust Dmitry A. Soshnikov corrected: Zeroglif released:; Updated: 2010-03-07http: // pushed /? P = Part 1 sentence reference: Chinese Translation of justin

For more details, what is this in ECMAScript?

Definition

This is an attribute in the execution context:

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

VO is the variable object 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 the context is entered and remains unchanged during context running.

Let's take a closer look at these cases:

This in Global Code

Everything is simple here. In the global code, this is always a global object, which may be referenced indirectly.

// Display the attributes of the global object defined this. a = 10; // global. a = 10 alert (a); // 10 // by assigning a value to an unlabeled implicit B = 20; alert (this. b); // 20 // It is also implicitly declared through the variable declaration // because the global context variable object is the global object itself var c = 30; alert (this. c); // 30
This in Function Code

It is interesting to use this in function code, which is difficult and can cause many problems.

In this type of code, the primary feature (or perhaps the most important) of this value is that it is not statically bound to a function.

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

In any case, the this value remains unchanged during code runtime, that is, because it 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 and can be changed continuously during runtime ).

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 no error occurs, it should be 10 instead of 20 }}; // when the context is entered, // this is treated as a bar object // determined as "bar" object; why so-will // be discussed below in detail bar. test (); // true, 20 foo. test = bar. test; // However, this is still not foo // although the same function foo is called. test (); // false, 10

There are several factors that affect the change of this value in the function code:

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.

To accurately determine this value under any circumstances, it is necessary to understand and remember this important point. It is exactly the way the function is called that affects the value of this in the context of the call, and there is nothing else (we can see in some articles, even in books about javascript, they claim: "this value depends on how the function is defined. If it is a global function, this is set as a global object. If the 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 in Different calling methods. These different calling methods lead to different this values.

Function foo () {alert (this);} foo (); // global alert (foo = foo. prototype. constructor); // true // but different call expressions of the same function, this is different from foo. prototype. constructor (); // foo. prototype

It is possible to call functions as some object-defined methods, but this will not be set as this object.

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 call expressions of the same function. this is different from exampleFunc (); // global, false

So how does the function call affect the value of this? To fully understand the determination of this value, you need to analyze one of its internal types in detail-Reference type ).

Reference type)

With pseudo-code, we can represent the value of the reference type as an object with two properties-base (that is, the object with the property) and propertyName in base.

var valueOfReferenceType = {  base: <base object>,  propertyName: <property name>};

There are only two types of reference values:

  1. When we process a identifier
  2. Or an attribute accessor

The processing process of the identifier is discussed in detail in the next article. Here we only need to know that in the return value of this algorithm, always a value of the reference type (which is important to 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:

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

To get the true value of an object from the reference type, the GetValue method in 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)); }

The internal [[Get] method returns the true values of object attributes, including the analysis of inherited attributes in the prototype chain.

GetValue(fooReference); // 10GetValue(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 of intermediate calculation, we have a value of the reference type.

var fooBarReference = {  base: foo,  propertyName: 'bar'}; GetValue(fooBarReference); // function object "bar"

How is the reference type value related to the this value in the function context? -- In the most important sense. This association process is the core 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 determined by the method of calling the function. If the left side of the brackets () is the reference type value, this is set to the base object of the reference type value ), in other cases (any other attribute that is different from the reference type), the value 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.Note: In ECMAScript 5th, the value is undefined instead of being converted to a global variable.

Let's take a look at the performance in this example:

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 ).

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:

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

We have a reference type again. Its base is a foo object, which is used as this when the function bar is activated.

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

However, to activate the same function in another form, we get other this values.

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

Because test is used as the identifier, other values of the reference type are generated, and its base (Global Object) is used as the this value.

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

Now, we can clearly tell you why activating the same function in different forms of expressions has different this values. The answer lies in the different intermediate values of the Reference type Reference.

Function foo () {alert (this);} foo (); // global, because var fooReference = {base: global, propertyName: 'foo '}; alert (foo = foo. prototype. constructor); // true // another form of 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:

function foo() {  alert(this.bar);} var x = {bar: 10};var y = {bar: 20}; x.test = foo;y.test = foo; x.test(); // 10y.test(); // 20
Function call and non-reference type

Therefore, as we have already pointed out, when the left side of the call bracket is not a reference type but another type, this value is automatically set to null and the result is a global object.

Let's think about this expression:

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

In this example, we have a function object, but it is not a reference type object (it is neither a identifier nor an attribute accessor). Correspondingly, this value is finally set as a global object.

More complex examples:

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 is there an attribute accesser whose median value should be a value of the reference type? In some calls, the value of this is not a base object, but a global object?

The problem lies in the following three calls. After a certain operation is applied, the value on the left of the call bracket is not of the reference type.

  1. The first example is obvious-the obvious reference type. The result is that this is the base object, that is, foo.
  2. In the second example, the group operator is not applicable. Think about the method mentioned above to obtain the true value of an object from the reference type, such as GetValue. Correspondingly, in the return of group operations, we still get a reference type. This is why this value is set as the base object again, that is, foo.
  3. In the third example, unlike the group operator, the value assignment operator calls the GetValue method. The returned result is a function object (but not a reference type), which means that this is set to null and the result is a global object.
  4. The fourth and fifth are the same-the comma operator and the logical operator (OR) Call the GetValue method. Correspondingly, we get the function without referencing it. And set it to global again.
The reference type and this are null.

One case is that when the call expression limits the reference type value on the left of the call bracket, although this is set to null, the result is implicitly converted to global. This situation occurs when the base object of the reference type value is an active 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 know in Chapter 12th, local variables, internal functions, and form parameters are stored in the activation object of a given function.

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

The activity object is always returned as this, and the value is null -- (that is, the AO. bar () of the pseudo code is equivalent to null. bar ()). Here we will go back to the example described above and set this as a global object.

Except for one case: If the with object contains a function name attribute, call the function in the internal block of the with statement. The With statement is added to the frontend of the object scope, that is, before the activity object. Correspondingly, there is a reference type (via the identifier or attribute accessors). Its base object is no longer an active object, but an object of the with statement. By the way, it 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 active object) in the scope chain) it must be on the front.

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: 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. In this way, this points to a global object in a specific activity object. Instead of catch objects.

Try {throw function () {alert (this) ;};} catch (e) {e (); // In ES3 standard, it is _ catchObject, ES5 standard is global} // on idea var eReference = {base: _ catchObject, propertyName: 'E'}; // this bug has been fixed in the new ES5 standard, // so this is the global Object 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 15th Functions. In the first call of a function, the base object is a parent activity object (or 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, this always points to a global object.

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

Another case related to this value is in the context of the function, which is a constructor call.

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

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

Manually set this in function call

The two methods defined in the function prototype (so all functions can access it) allow you to manually set the this value for function calls. They are. apply and. call methods. They use the first accepted parameter as the value of this, which is used in the scope of the call. The difference between the two methods is very small. For. apply, the second parameter must be an array (or an object similar to an array, such as arguments, in turn,. call can accept any parameter. The required parameters of the two methods are the first -- this.

For example:

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 == 30a.apply({b: 30}, [40]) // this === {b: 30}, this.b == 30, c == 40
Conclusion

In this article, we discuss the features of the this keyword in ECMAScript (compared with C ++ and Java, they are indeed characteristic ). I hope this article will help you understand exactly how the this keyword works in ECMAScript. Similarly, I am happy to return to your question in the comments.

Copyright statement: This article is the original author of the http://www.zuiniusn.com, not allowed by the blogger can not be reproduced.

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.