This pointer of Javascript is very easy to understand and use 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 Javascript this pointer example:
-
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:
-
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:
-
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 the this pointer of Javascript
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 the embodiment of sayHi, 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. Therefore, this. 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 just similar to two pointers and actually point 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 this. the name references all the variables declared but not defined in the undefined object Javascript to point to the undefined object). The second time we added the name attribute when defining the person, then this. the name points to the defined string.
After understanding the above, we will transform the last example to object-oriented code.
-
In the above Code, we defined a "class" of Person as an object), and then the prototype of this class is equivalent to the concept of static member variables in C ++) defines the sayHello attribute 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:
-
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
- "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:
-
In this case, the 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.
- "BtnTest" Type = "Button" Value = "Click me" Onclick = "SayHi (this )" > The equivalent code is as follows:
-
Example 2 -- this pointer is lost due to temporary variables
-
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?
-
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:
No temporary variables are introduced. Utility is used each time. getCookie is used to call the getCookie function. decode explicitly references the decode object without using this pointer to implicitly reference it. If Utility is an instantiated object, that is, generated by 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:
-
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:
-
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 and copy the passed parameters. Note that all values in Javascript are passed, does not reference the concept passed) to 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
-
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, save the Javascript 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
-
Actually, the prototype method is essentially the same.
The solution of the famous Extjs library adopts the same method as that of Microsoft.
- JavaScript class and inheritance: prototype attribute
- JavaScript class and inheritance: this property
- Summary of three + 1 implementation methods of ExtJS Grid Tooltip
- Implementation of JavaScript asynchronous call framework chain
- JQuery-style chained call of JavaScript asynchronous call framework