In-depth understanding of JavaScript Internal principles (3): This

Source: Internet
Author: User

This article is a translation http://dmitrysoshnikov.com/ecmascript/chapter-3-this/

Summary

This article will further discuss the concepts closely related to the execution context --ThisKeyword.

Facts prove that,ThisThe content of this item is very complicated. It will have different values in different execution contexts, and will cause some problems accordingly.

ManyProgramOnce you seeThisKeyword, it will be associated with the object-oriented programming method, it points to the new object created by the constructor. Ecmascript also supports this. However, as we all know, this is not only used to represent the created object.

Next we will unveil this secret in ecmascript.

Definition

ThisIs an attribute of the execution context:

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

Vo is the variable object introduced in the previous chapter.

This and context executableCodeThe value is determined when it enters the context stage, and cannot be changed during the code execution stage.

Let's take a detailed look at how this works in ecmascript.

This value in Global Code

In this case, everything becomes very simple, and the value of this is always the global object itself; therefore, You can indirectly obtain references:

 
// Explicitly define the attributes of the global object this. A = 10; // global. A = 10 alert (a); // 10 // implicitly defines B = 20 by assigning a value to an unrestricted identifier; alert (this. b); // 20 // implicit definition through variable declaration // because the variable object in the global context is the global object itself var c = 30; alert (this. c); // 30
This value in Function Code

WhenThisIn function code, things become much more interesting. In this case, it is the most complex and causes many problems.

The first feature of this value in Function Code (and also the most important feature) is:It is not statically bound to a function.

As mentioned earlier,The value of this is determined at the context stage. In function code, the value of this is different each time..

However,Once the code is executed, the value cannot be changed..For example, it is impossible to assign a new value to this, because this is not a variable at all.(In Python, it displays the definedSelfObjects can be changed at runtime ):

VaR Foo = {X: 10}; var bar = {X: 20, test: function () {alert (this = bar); // true alert (this. x); // 20This = Foo; // error. The value of this cannot be changed.Alert (this. x); // if there is no error, the value is 10 instead of 20}; // when entering the context, the value of this determines the "bar" object. // The bar will be detailed later. test (); // true, 20 Foo. test = bar. test; // However, at this time, the value of this will change to "foo" // even if we call the same function Foo. test (); // false, 10

Therefore, there are many factors that affect this value in function code.

First, in normal function callsThe value of this is determined by the caller who activates the context Code. For example, the outer context of the called function. The value of this is determined by the form of the call expression.

Understanding and Keeping this in mind is necessary to accurately determine the value of this in any context.

The value of this in the call context may only be in the form of a call expression, that isHow to call a function. (Some JavascriptArticleAnd"ThisThe value of this depends on the function definition method. If it is a global function, the value of this is set as a global object. If it is an object method, then the value of this will be set to this object. "-- this is purely nonsense, and it is simply a false positive ). As we have seen before, even if it is a global function, the value of this varies with the function call method:

Function Foo () {alert (this);} Foo (); // global alert (FOO = Foo. prototype. constructor); // true // However, the value of this is different for the same function in another call method. prototype. constructor (); // Foo. prototype

When calling a method of an object, the value of this may not be the object:

 
VaR Foo = {bar: function () {alert (this); alert (this = Foo) ;}}; Foo. bar (); // Foo, true var examplefunc = Foo. bar; alert (examplefunc = Foo. bar); // true // Similarly, if the same function is called in different ways, the value of this is different from that of examplefunc (); // global, false

So how does the method of calling an expression affect the value of this? In order to fully understand the mysteries of this, first of all, it is necessary to introduce an internal type-reference type (ReferenceType ).

Reference Type

The value of the reference type can be expressed as an object with two attributes using pseudo code --BaseAttribute (the object to which the attribute belongs) andPropertynameAttribute:

 
VaR valueofreferencetype = {base:, propertyname :};

The value of the reference type can only be in the following two cases:

    1. When processing an identifier
    2. Or when accessing the attributes

The processing of identifiers is described in Chapter 4-The domain chain used.AlgorithmReturns a value of the reference type (this is critical to the value of this ).

Identifiers are actually variable names, function names, function parameter names, and unrestricted attributes of global objects.. As follows:

 
VaR Foo = 10; function bar (){}

In the intermediate process, the value of the corresponding reference type is as follows:

VaR fooreference = {base: Global, propertyname: 'foo'}; var barreference = {base: Global, propertyname: 'bar '};

To obtain the actual value of an object from the value of the reference type, you mustGetvalueMethod, which can be described as follows using pseudocode:

Function getvalue (value) {If (type (value )! = Reference) {return value;} var base = getbase (value); If (base = NULL) {Throw new referenceerror;} return base. [[get] (getpropertyname (value ));}

In the above Code[[Get]The method returns the actual value of the object property, including the property inherited from the prototype chain:

 
Getvalue (fooreference); // 10 getvalue (barreference); // function object "bar"

There are two methods for Attribute access:Point Symbol(At this time, the property name is the correct identifier and is known in advance) orBrackets:

 
Foo. Bar (); Foo ['bar'] ();

The following reference type value is obtained during the intermediate process:

 
VaR foobarreference = {base: Foo, propertyname: 'bar'}; getvalue (foobarreference); // function object "bar"

Again, how does the value of the reference type affect the value of this in the context of the function? -- Very important. This is also the focus of this article. In general, the rules for determining the value of this in the context of a function are as follows:

In the context of a function, the value of this is provided by the function caller and determined by the form of the current call expression. If there is a reference type value on the left of the call bracket (), the value of this will be set as the Base Object of the reference type value. In all other cases (non-reference type), the value of this is alwaysNull. However, since null is meaningless for this, it is implicitly converted to a global object.

As follows:

Function Foo () {return this;} Foo (); // global

In the above Code, the left side of the call bracket is the reference type value (becauseFooIs an identifier ):

VaR fooreference = {base: Global, propertyname: 'foo '};

Correspondingly, the value of this will be set as the Base Object of the reference type value, which is a global object.

Attribute access is similar:

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

Similarly, it is a reference type value. Its base object is the foo object. When the bar function is activated, the value of this is set to the foo object:

 
VaR foobarreference = {base: Foo, propertyname: 'bar '};

However, if the same function is activated in different ways, the value of this is completely different.:

 
VaR test = Foo. Bar; test (); // global

Because test is also an identifier, a value of another reference type is generated. The base object (Global Object) is the value of this:

VaR testreference = {base: Global, propertyname: 'test '};

At this point, we can explain exactly why the same function is activated in different call methods, and the value of this will be different-the answer is that during the processing, is a value of different reference types:

Function Foo () {alert (this);} Foo (); // global, because var fooreference = {base: Global, propertyname: 'foo '}; alert (FOO = Foo. prototype. constructor); // true // another call method Foo. prototype. constructor (); // Foo. prototype, because var fooprototypeconstructorreference = {base: Foo. prototype, propertyname: 'constructor '};

The following is another (typical) Example of dynamically determining the value of this using the call expression:

 
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

As mentioned earlier, when the left side of the call bracket is not of reference type, the value of this is set to null and eventually becomes a global object.

Let's consider the following expression:

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

In the above example, there is a function object, but it is not a reference type object (because it is neither an identifier nor an attribute access), so the value of this is finally set as a global object.

The following is a more complex example:

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?

After reading the above Code, you may have another question: Why is this attribute access clearly, but the final value of this is not a base object but a global object?

The main question here is the last three expressions. After these three expressions are added with a specific operation, the left side of the call bracket is no longer a reference type value.

The first case -- very explicit, is the reference type, and the final value of this is set to base object, foo.

In the second case, there is a group of operators (Grouping Operator), This operator does not trigger calls to obtain the actual value of the reference type, for example:GetvalueMethod. Correspondingly, in the process of processing the group operator, the value is still a reference type value, which explains why the value of this is set to a base object,Foo.

The third case isValue assignment operator(Assignment operator), Unlike the group operator, it triggers the call to the getvalue method (see step 3 in 11.13.1 ). The final returned result is a function object (instead of a reference type value), which means that the value of this will be set to null and eventually become a global object.

The fourth and fifth cases are similar-both the comma operator and the or logical expression will trigger the call to the getvalue method, so the original reference type value will be lost and changed to the function type accordingly, the value of this becomes a global object.

Reference Type and null (value of this)

In this case, when the call expression has a reference type value on the left, but the value of this is null, it is eventually changed to a global object. The condition is that when the base object of the reference type value happens to beActive Object.

This happens when the internal sub-function is called in the parent function. As described in Chapter 2, local variables, internal functions, and function parameters are stored inActive ObjectMedium:

 
Function Foo () {function bar () {alert (this); // global} bar (); // The same as AO. Bar}

An active object always returns the value of this -- null (expressed in pseudo code, ao. Bar () is equivalent to null. Bar ()). Then, as described earlier, the value of this will eventually change from null to a global object.

When a function call is included inWithIn the code block of the statement, andWithWhen an object contains a function attribute, exceptions may occur.WithStatement will add this object to the beginning of the scope chain, before the active object. Correspondingly, when a value of the reference type (identifier or attribute access) is used, the base object is no longer an active object, but an object of the with statement. In addition, it is worth mentioning that it is not only for internal functions, but also for global functions, because the with object masks higher-level objects (global objects or active objects) in the scope chain):

 
VaR x = 10; with ({FOO: function () {alert (this. x) ;}, X: 20}) {Foo (); // 20} // because var fooreference = {base: _ withobject, propertyname: 'foo '};

When the called function happens to beCatchClause parameters are similar: in this case,CatchObjects are also added to the beginning of the scope chain before the active objects and global objects. However, this behavior is identified as a bug in the ECMA-262-3 and has been corrected in the ECMA-262-5; therefore, in this case, the value of this should be set as a global object, instead of catch objects.

 
Try {Throw function () {alert (this) ;};} catch (e) {e (); // _ catchobject-In ES3, global-fixed in es5} // on idea var ereference = {base: _ catchobject, propertyname: 'E'}; // However, since this is a bug //, it should be forced to set it to a global object // null => global var ereference = {base: Global, propertyname: 'E '};

In the same case, a non-Anonymous function is called recursively (the function-related content will be introduced in chapter 5 ). When the nth function is called, the base object is the outer active object (or global object ), in the next recursive call, the base object should be a special object that stores the name of an optional function expression. However, in this case, the value of this is always a global object:

 
(Function Foo (bar) {alert (this );! Bar & Foo (1); // "shocould" be special object, but always (correct) Global}) (); // global
This value when a function is called as a constructor

Here we will introduce another situation about this value in the context of a function -- when a function is called as a constructor:

Function A () {alert (this); // newly created object, below-"A" object this. X = 10;} var A = new A (); alert (. x); // 10

In this case, the new operator calls the internal [[construct] of the "A" function. After an object is created, the internal [[Call] function is called, and the value of this in all "A" functions is set as the newly created object.

Manually set the value of this for function calls.

Function. PrototypeTwo methods are defined (so they are accessible to all functions), allowing you to manually specify the value of this when the function is called. The two methods are:. ApplyAnd. Call;Both accept the first parameter as the value of this in the call context. Their differences do not matter:. ApplyFor example, the second parameter accepts the array type (or the object of the class array, suchArguments), And. CallThe method accepts any number of parameters. Only the first parameter is required for the two methods -- the value of this.

As follows:

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

This article discusses ecmascriptThisKeyword features (compared with C ++ or Java, they can be said to be features ). This article helps you understand how this keyword works in ecmascript.

Additional reading
    • This
    • This keyword
    • New operator
    • Function call
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.