Home >Web Front-end >JS Tutorial >In-depth analysis of the use of this keyword in JavaScript programming_Basic knowledge
What exactly does this in JavaScript mean? Many people will tell you that this refers to the current object. Is this correct? That's true in most cases. For example, we often write JavaScript like this on web pages:
<input type="submit" value="提交" onclick="this.value='正在提交数据'" />
This here obviously refers to the current object, which is the submit button. Usually, the situation when we use this is similar to this. But is there any situation where this is not the case?
Look at this example:
var foo = function() { console.log(this); } foo(); new foo();
Compare the execution results of foo() and new foo(). You will find that in the former, this does not point to foo itself, but to the window object of the current page, while the latter really points to foo. Why is this?
In fact, this involves an important feature of JavaScript, which is the so-called "closure". The concept of closure is not complex, but it is not so simple that it can be explained clearly in one or two sentences. I will delve into this most important feature of Javascript in a future article. Now, what I want to tell you is that scope in JavaScript becomes very important because of closures.
The so-called scope, simply put, refers to the environment in which a function is created. The value of this variable, if not specified, is the current scope of the function.
In the previous example, the foo() function is in the global scope (here is the window object), so the value of this is the current window object. In the form of new foo(), a copy of foo() is actually created and operations are performed on this copy, so this here is the copy of foo().
This may be a bit abstract, let’s take a look at a practical example:
<input type="button" id="aButton" value="demo" onclick="" /> <script type="text/javascript"> function demo() { this.value = Math.random(); } </script>
If you call the demo() function directly, the program will report an error because the demo function is defined in the window object, so the owner (scope) of demo is window, and the this of demo is also window. The window does not have a value attribute, so an error was reported.
If we add a copy of this function to an HTML element by creating a copy, then its owner becomes this element, and this also refers to this element:
document.getElementById("aButton").onclick = demo;
This sets the onlick attribute of aButton to a copy of demo(), and this also points to aButton.
You can even create different copies of the function for multiple different HTML elements. The owner of each copy is the corresponding HTML element, and their respective thiss also point to their owners, which will not cause confusion.
However, if you define the onlick event of an element like this:
<input type="button" id="aButton" value="demo" onclick="demo()" />
After clicking this button, you will find that the program will report an error again - this points to the window again!
Actually, this method does not create a function for the program, but only references the function.
Let’s take a closer look at the differences.
Use the method that creates a copy of the function:
<input type="button" id="aButton" value="demo" /> <script type="text/javascript"> var button = document.getElementById("aButton"); function demo() { this.value = Math.random(); } button.onclick= demo; alert(button.onclick); </script>
The resulting output is:
function demo() { this.value = Math.random(); }
How to use function references:
<input type="button" id="aButton" value="demo" onclick="demo()" />
The resulting output is:
function onclick() { demo(); }
You can see the difference this way. In the function reference method, the onclick event just calls the demo() function directly, and the scope of the demo() function is still the window object, so this still points to the window.
This raises another question: Since function copies are so easy to use, why do we need function references? The answer is performance. Each time a copy of a function is created, the program will allocate a certain amount of memory for the copy of the function. In actual applications, most functions are not necessarily called, so this part of memory is wasted. Using function references, the program will only allocate memory to the function itself, while references only allocate pointers, which is much more efficient. Programmers, saving is the main thing, eh
So let’s look at a better solution:
<script type="text/javascript"> function demo(obj) { obj.value = Math.random(); } </script> <input type="button" value="demo" onclick="demo(this)" /> <input type="button" value="demo" onclick="demo(this)" /> <input type="button" value="demo" onclick="demo(this)" />
这样,效率和需求就都能兼顾了。
this的指向
JavaScript由于其在运行期进行绑定的特性,JavaScript 中的 this 可以是全局对象、当前对象或者任意对象,这完全取决于函数的调用方式。JavaScript 中函数的调用有以下几种方式:作为对象方法调用,作为函数调用,作为构造函数调用,和使用 apply 或 call 调用。常言道,字不如表,表不如图。为了让人更好的理解JavaScript this 到底指向什么?下面用一张图来进行解释:
上图我称之为”JavaScript this决策树“(非严格模式下)。下面通过例子来说明这个图如何来帮助我们对this进行判断:
var point = { x : 0, y : 0, moveTo : function(x, y) { this.x = this.x + x; this.y = this.y + y; } }; //决策树解释:point.moveTo(1,1)函数不是new进行调用,进入否决策, //是用dot(.)进行调用,则指向.moveTo之前的调用对象,即point point.moveTo(1,1); //this 绑定到当前对象,即point对象
point.moveTo()函数在 “JavaScript this决策树“中进行判定的过程是这样的:
1)point.moveTo函数调用是用new进行调用的么?这个明显不是,进入“否”分支,即函数是否用dot(.)进行调用?;
2)point.moveTo函数是用dot(.)进行调用的,即进入“是”分支,即这里的this指向point.moveTo中.之前的对象point;
图解point.moveTo函数的this指向什么的解析图如下图所示:
再举例,看下面的代码:
function func(x) { this.x = x; } func(5); //this是全局对象window,x为全局变量 //决策树解析:func()函数是用new进行调用的么?为否,进入func()函数是用dot进行调用的么?为否,则 this指向全局对象window x;//x => 5
func()函数在 “JavaScript this决策树“中进行判定的过程是这样的:
1)func(5)函数调用是用new进行调用的么?这个明显不是,进入“否”分支,即函数是否用dot(.)进行调用?;
2)func(5)函数不是用dot(.)进行调用的,即进入“否”分支,即这里的this指向全局变量window,那么this.x实际上就是window.x;
图解func函数的this指向什么的解析图如下图所示:
针对作为函数直接调用的方式,下面看一个复杂的例子:
var point = { x : 0, y : 0, moveTo : function(x, y) { // 内部函数 var moveX = function(x) { this.x = x;//this 指向什么?window }; // 内部函数 var moveY = function(y) { this.y = y;//this 指向什么?window }; moveX(x); moveY(y); } }; point.moveTo(1,1); point.x; //=>0 point.y; //=>0 x; //=>1 y; //=>1
point.moveTo(1,1)函数实际内部调用的是moveX()和moveY()函数, moveX()函数内部的this在 “JavaScript this决策树“中进行判定的过程是这样的:
1)moveX(1)函数调用是用new进行调用的么?这个明显不是,进入“否”分支,即函数是否用dot(.)进行调用?;
2)moveX(1)函数不是用dot(.)进行调用的,即进入“否”分支,即这里的this指向全局变量window,那么this.x实际上就是window.x;
下面看一下作为构造函数调用的例子:
function Point(x,y){ this.x = x; // this ? this.y = y; // this ? } var np=new Point(1,1); np.x;//1 var p=Point(2,2); p.x;//error, p是一个空对象undefined window.x;//2
Point(1,1)函数在var np=new Point(1,1)中的this在 “JavaScript this决策树“中进行判定的过程是这样的:
1)var np=new Point(1,1)调用是用new进行调用的么?这个明显是,进入“是”分支,即this指向np;
2)那么this.x=1,即np.x=1;
Point(2,2)函数在var p= Point(2,2)中的this在 “JavaScript this决策树“中进行判定的过程是这样的:
1)var p= Point(2,2)调用是用new进行调用的么?这个明显不是,进入“否”分支,即函数是否用dot(.)进行调用?;
2)Point(2,2)函数不是用dot(.)进行调用的?判定为否,即进入“否”分支,即这里的this指向全局变量window,那么this.x实际上就是window.x;
3)this.x=2即window.x=2.
最后看一下函数用call 和apply进行调用的例子:
function Point(x, y){ this.x = x; this.y = y; this.moveTo = function(x, y){ this.x = x; this.y = y; } } var p1 = new Point(0, 0); var p2 = {x: 0, y: 0}; p1.moveTo.apply(p2, [10, 10]);//apply实际上为p2.moveTo(10,10) p2.x//10
The process of the p1.moveTo.apply(p2,[10,10]) function in "JavaScript this decision tree" is as follows:
We know that the two methods apply and call are extremely powerful. They allow switching the context of function execution, that is, the object bound to this. p1.moveTo.apply(p2,[10,10]) is actually p2.moveTo(10,10). Then p2.moveTo(10,10) can be interpreted as:
1) Is the p2.moveTo(10,10) function called using new? This is obviously not the case. Go to the "No" branch, that is, is the function called with dot(.)? ;
2) The p2.moveTo(10,10) function is called with dot(.), that is, it enters the "yes" branch, that is, this here points to the previous object p2 in p2.moveTo(10,10). , so p2.x=10;
Regarding the process of JavaScript function execution environment, there is a very good description in the IBM developerworks document library. The excerpt is as follows:
“Function in JavaScript can be executed as an ordinary function or as a method of an object. This is the main reason why this has such rich meaning. When a function is executed, an execution environment (ExecutionContext) is created , all the behavior of the function occurs in this execution environment. When building the execution environment, JavaScript will first create the arguments variable, which contains the parameters passed in when calling the function. Then it will create the scope chain and initialize it first. The formal parameter list of the function, the value is the corresponding value in the arguments variable. If there is no corresponding value in the arguments variable, the formal parameter is initialized to undefined. If the function contains internal functions, these internal functions are initialized. If not, continue to initialize. For the local variables defined in this function, it should be noted that these variables are initialized to undefined at this time, and their assignment operations will not be executed until the function is executed after the execution environment (ExecutionContext) is successfully created. This is very important for us to understand the role of variables in JavaScript. The domain is very important. In view of the space, we will not discuss this topic here. Finally, assign a value to this variable. As mentioned above, it will be assigned to this global object, current object, etc. according to the function calling method (up to this point). ExecutionContext) is created successfully, the function begins to execute line by line, and the required variables are read from the previously constructed execution environment (ExecutionContext) ”
.
Understanding this paragraph will go a long way in understanding Javascript functions.