Javascript this pointer

Source: Internet
Author: User

Preface

Javascript is an object-based Dynamic Language. That is to say, everything is an object. A typical example is that a function is considered a common object. Javascript can implement object-oriented programming through certain design patterns. Among them, this "pointer" is an important feature for implementing object-oriented programming. However, this is also a very easy-to-understand error in Javascript, and then uses the wrong feature. This is especially true for comrades who have been engaged in static languages for a long time.

Example

Let's take a look at the simplest example:

<Script Type= "Text/javascript">  VarName ="Kevin Yang";FunctionSayHi () {alert ("Hello, my name is"+ Name);} sayHi ();Script>

This code is simple. We define a global String object name and function object sayHi. A "Hello, my name is Kevin Yang" dialog box appears ".

Let's change this code slightly:

<Script Type= "Text/javascript">  VarName ="Kevin Yang";FunctionSayHi () {alert ("Hello, my name is"+This. Name) ;}sayhi ();Script>

The difference between this code and the previous Code is that the sayHi function adds this. prefix when using name. The running result is the same as above. This indicates that this. name references a global name object.

We don't mean at the beginning. A function is also a common object and can be used as a common variable. Let's change the above Code:

<Script Type= "Text/javascript">  VarName ="Kevin Yang";FunctionSayHi () {alert ("Hello, my name is"+This. Name );}VarPerson = {}; person. sayHello = sayHi; person. sayHello ();Script>

This time, we created a global object person and assigned the sayHi function object to the sayHello attribute of the person object. The running result is as follows:

The content of this greeting is a bit undefined. We found that this. name has become undefined. This indicates that the this. name object cannot be found during internal execution of the sayHello function. What if we re-define the person object and add a name attribute to it?

var person = {name:"Marry"};

Run the code to find that the greeting "person" has changed:

Is there a path?

Guiding principles for determining this pointer

In Javascript, this pointer indicates the owner of the object that executes the current code.

In the above example, we can see that for the first time, we defined a global function object sayHi and executed this function. The function uses the this keyword internally, so the object executing this line of code is sayHi (the embodiment of everything), and sayHi is defined in the global scope. In Javascript, the so-called global object is nothing more than an attribute defined under the root object window. Therefore, the owner of sayHi is the window object. That is to say, in the global scope, you can directly use name to reference this object, or you can use window. name to reference the same object. ThereforeThis. name can be translated into window. name..

Let's look at the second example of this. We define a person object and its sayHello attribute to point to the sayHi global object. Then, when we run person. when sayHello is used, the object to which this code belongs is sayHello (in fact, sayHi and sayHello are actually similar to two pointers, pointing to the same object ), the owner of the sayHello object is person. For the first time, there is no name attribute in person, so the pop-up dialog box is that this. name references the undefined object (All variables declared and not defined in Javascript point to the undefined object.The second time we added the name attribute when defining person, this. name points to the defined string.

After understanding the above, we will transform the last example to object-oriented code.

<Script Type= "Text/javascript">  VarName ="Kevin Yang";FunctionSayHi () {alert ("Hello, my name is"+This. Name );}FunctionPerson (name ){This. Name = name;} Person. prototype. sayHello = sayHi;VarMarry =NewPerson ("Marry"); Marry. sayHello ();VarKevin =NewPerson ("Kevin"); Kevin. sayHello ();Script>

In the above Code, we define a Person "class" (actually an object), and then in the prototype of this class (The class prototype is equivalent to the concept of static member variables in C ++.) To point to the global sayHi object. Running the code, we can see that both marry and kevin successfully say "hello" to us ".

There are two points to think about in this Code. One is that we are familiar with new, but what operations have new done here? The other one is, when sayHello is executed here, why does this pointer correctly point to the marry and kevin objects?

Let's re-"translate" the operations that define "class" and instantiate class objects above:

<Script Type= "Text/javascript">  VarName ="Kevin Yang";FunctionSayHi () {alert ("Hello, my name is"+This. Name );}FunctionPerson (name ){Var This;This. Name = name;Return This;} Person. prototype. sayHello = sayHi;VarMarry = Person ("Marry"); Marry. sayHello ();VarKevin = Person ("Kevin"); Kevin. sayHello ();Script>

Of course, this Code cannot be correctly executed, but it can help you better understand this process.

When we use the new keyword to instantiate a "class" object, the Javascript engine defines a new object within the object and saves it to the this pointer. All the code that uses this inside this object actually points to this new object. For example, this. name = name is to assign the name object in the parameter to the newly created object. After the function object is executed, the Javascript engine returns this object to you, so the name of the object obtained by the marry variable is "Marry ", the name attribute of the object obtained by the kevin variable is "Kevin ".

Misuse-prone situations

After understanding this pointer, let's take a look at some situations where it is easy to misuse this pointer.

Example 1: An event handler that binds Dom elements in inline mode

<Script Type= "Text/javascript">  FunctionSayHi () {alert ("The currently clicked element is"+This. TagName );}Script><Input Id= "BtnTest" Type= "Button" Value= "Click me" Onclick= "SayHi ()">

In this sample code, we bind a button click event and expect to print the Tag Name of the click element in the pop-up dialog box. However, the running result is:

That is, the this pointer does not point to the input element. This is because when an event handler function is bound to a Dom element in inline mode, the following code is actually executed:

<script type="text/javascript">    document.getElementById("btnTest").onclick = function(){    sayHi();  }script>

In this caseThe ownership of the sayHi function object is not transferred or belongs to the window.With the above guidelines, we can understand why this. tagName is undefined.

What if we want to reference the element itself?

As we know, the onclick function belongs to the btnTest element. In this function, the this pointer points to this Dom object, so we only need to pass this as the parameter to sayHi.

<Script Type= "Text/javascript">  FunctionSayHi (el) {alert ("The currently clicked element is"+ El. tagName );}Script><Input Id= "BtnTest" Type= "Button" Value= "Click me" Onclick= "SayHi (this )">

The equivalent code is as follows:

<script type="text/javascript">   document.getElementById("btnTest").onclick = function(){    sayHi(this);  }script>

Example 2 -- this pointer is lost due to temporary variables

<Script Type= "Text/javascript">  VarUtility = {decode:Function(Str ){ReturnUnescape (str) ;}, getCookie:Function(Key ){//... Omitting the code used to extract the cookie string      VarValue ="I % 27 m % 20a % 20 cookie";Return This. Decode (value) ;}}; alert (Utility. getCookie ("Identity"))Script>

When writing a slightly larger Js library, we usually encapsulate a Utility class by ourselves, and then use some common functions as attributes of the Utility class, for example, the getCookie function and decoder function that are frequently used by the client. If each function is independent from each other, it is easy to solve. The problem is that functions are sometimes referenced to each other. For example, the preceding getCookie function decode the string extracted from document. cookie before returning it. If we call it through Utility. getCookie, there is no problem. We know that the this pointer inside getCookie still points to the Utility object, while the Utility object contains the decode attribute. The code can be successfully executed.

But do some people accidentally use the Utility object like this?

<Script Type= "Text/javascript">  FunctionShowUserIdentity (){// Save the getCookie function to a local variable, because it is often used below    VarGetCookie = Utility. getCookie; alert (getCookie ("Identity");} ShowUserIdentity ();Script>

When the code is run, an exception "this. decode is not a function" is thrown ". Using the guiding principles we mentioned above, it is easy to understand, because Utility. the getCookie object is assigned to the Temporary Variable getCookie, and the temporary variable belongs to the window object. However, the temporary variable cannot be directly referenced by the outside world, only visible to the Javascript engine -- the this pointer inside the getCookie function points to the window object, and the window object does not define a decode function object, therefore, this exception will be thrown.

This problem is caused by the introduction of temporary variables. There are several solutions to this problem:

  • Do not introduce temporary variables. Every time you use them, use Utility. getCookie to call them.
  • The getCookie function uses Utility. decode to explicitly reference the decode object without using the this pointer for implicit reference. (If Utility is an instantiated object, which is generated through new, this method is unavailable)
  • Use the Funtion. apply or Function. call Function to specify the this pointer.

The preceding two methods are easy to understand. The third method needs to be mentioned. Because the point of this pointer is easily transferred and lost, Javascript provides two similar functions, apply and call, to allow the function to explicitly specify this pointer during calls.

The corrected code is as follows:

<Script Type= "Text/javascript">  FunctionShowUserIdentity (){// Save the getCookie function to a local variable, because it is often used below    VarGetCookie = Utility. getCookie; alert (getCookie. call (Utility,"Identity"); Alert (getCookie. apply (Utility ,["Identity"]);} ShowUserIdentity ();Script>

Call and apply only have syntax differences and have no function differences.

Example 3 -- this pointer is lost when parameter passing through the Function

Let's take a look at the problem code:

<Script Type= "Text/javascript">  VarPerson = {name:"Kevin Yang", SayHi:Function() {Alert ("Hello, I am"+This. Name);} setTimeout (person. sayHi, 5000 );Script>

This code is expected to say hello to the visitor five seconds after the visitor enters the page. The setTimeout Function receives a function as a parameter and executes the function at the specified trigger time. However, after waiting for five seconds, the pop-up dialog box shows this. name is undefined.

In fact, this problem is similar to the problem in the previous example and is caused by temporary variables. When we execute a function, if the function has parameters, the Javascript engine will create a temporary variable andCopy (note that all values in Javascript are passed, and there is no reference transfer concept)Give this temporary variable. That is to say, the whole process is the same as a temporary variable for getCookie defined above, and then assign Utility. getCookie to this temporary variable. In this example, it is easy to ignore the bug caused by temporary variables.

Function object parameter passing

Currently, many frameworks have solutions to this pointer loss caused by function passing as parameters.

Prototype solution -- use the bind method to encapsulate the function and return the encapsulated object before passing the Parameter

<Script Type= "Text/javascript">  VarPerson = {name:"Kevin Yang", SayHi:Function() {Alert ("Hello, I am"+This. Name );}}VarBoundFunc = person. sayHi. bind (person, person. sayHi); setTimeout (boundFunc, 5000 );Script>

The implementation of the bind method actually uses the closure, another advanced feature of Javascript. Let's take a look at the source code:

function bind(){  if (arguments.length < 2 && arguments[0] === undefined)     return this;  var __method = this, args = $A(arguments), object = args.shift();  return function(){    return __method.apply(object, args.concat($A(arguments)));  }}

First, store this pointer to the Temporary Variable inside the function, and then reference this temporary variable in the returned function object to form a closure.

Solution provided by Microsoft's Ajax library-Build a delegate object

<Script Type= "Text/javascript">  VarPerson = {name:"Kevin Yang", SayHi:Function() {Alert ("Hello, I am"+This. Name );}}VarBoundFunc = Function. createDelegate (person, person. sayHi); setTimeout (boundFunc, 5000 );Script>

Actually, the prototype method is essentially the same.

The solution of the famous Extjs library adopts the same method as that of Microsoft.

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.