Home > Article > Web Front-end > Detailed explanation of this keyword in javascript
No matter what knowledge we learn, getting used to making a list of the knowledge we have learned will help us clarify our thinking and is a good learning method. highly recommended.
The following is a bit long, so I hope readers will read it patiently.
The following content will be divided into the following parts:
1. Meaning
1.1: The meaning of this
1.2: The variability of this point
2. Usage occasions
2.1: Global environment
2.2: Constructor
2.3: Methods of objects
3. Points to note when using
3.1: Avoid multi-level nesting of this
3.2: Avoid this in array processing methods
3.3: Avoid this in callback functions
1. Meaning
1.1: The meaning of this
As mentioned in a blog post I wrote about the relationship between the constructor and the new keyword, the new keyword always returns an object. This object can be the empty object returned when new calls the constructor, or it can be a complex data type (including objects, arrays, etc.) returned by using the return statement in the constructor.
Similarly, like the new keyword, the this keyword always returns an object. To be more specific, it is the object where the property or method "currently" resides.
var Keith = { firstName: 'Chou', describe: function() { return this.firstName; } }; console.log(Keith.describe()); //'Chou'
In the above code, this.firstName represents the firstName attribute of the object where the describe method is currently located. In other words, when the describe method is called in the global scope, the current object where the describe method is located is Keith, so Keith.firstName is called.
1.2: Variability pointed to by this
Since the properties of an object can be assigned to another object, the current object where the properties are located is variable. In other words, the pointer of this is variable.
var Keith = { firstName: 'Chou', describe: function() { return this.firstName; } }; var Rascal={ firstName: 'King' } Rascal.describe=Keith.describe; console.log(Rascal.describe()); //'King'
In the above code, the describe attribute in the Keith object is assigned to Rascal, so the current object in the describe method is Rascal, so this.firstName points to Rascal. Because it is passed by address, modifying firstName will have an impact on the original object. This example may not be easy to understand, so take a look at the following example.
function f(){ return this.firstName; } var Keith = { firstName: 'Chou', describe:f }; var Rascal={ firstName: 'King', describe:f } console.log(Keith.describe()); //'Chou' console.log(Rascal.describe()); //'King'
In the above code, the method is moved to the global scope, and the this keyword is used inside function f. As the object where f is located is different, this points to different objects.
In the global scope, the this keyword will point to the top-level object (that is, the window object).
var name='keith'; function person(){ var name='rascal'; return this.name; } console.log(person()); //'keith'
In the above code, keith is returned instead of rascal. The reason is that this points to the global scope. Defining a function in the global scope points to the window object by default, not the function itself. However, if you don't use var to declare a local variable inside the function, the results will be different.
var name='keith'; function person(){ name='rascal'; return this.name; } console.log(person()); //'rascal'
In the above code, var is not used to declare a local variable inside the function, so the name attribute inside the function at this time is not a local variable, but a global variable. Therefore, the previous name attribute will be overwritten. If you don’t know about local variables and global variables, you can visit this article.
As long as the function is assigned to another variable, the pointer of this will change.
var Keith={ name:'keith', describe:function(){ return this.name; } } var name='rascal'; var f=Keith.describe; console.log(f()) //'rascal'
In the above code, rascal is returned, not keith. Because Keith.describe is assigned to the f variable, and there is a name variable in the global scope, the this point of the function inside Keith will point to the object where f is running (the top-level object, that is, the window object)
Summary Let’s take a look:
1. In the JavaScript language, everything is an object (except undefined and null), and the running environment is also an object, so functions run in a certain object, and this is the object (environment).
2.The pointing of this is dynamic. If the function is in the global scope, then this will point to the global environment; if the function is in an object, then this will point to the object.
2. Usage occasions
This usage occasion can be divided into the following occasions.
2.1: Global environment (global scope)
Use this object in the global scope, which points to the top-level object, which is the window object.
function keith() { return (this === window) } console.log(keith()) //true
In the above code, whether it is inside a function or not, as long as it is run in the global scope, this points to the top-level object window.
2.2: Constructor
This in the constructor points to the object instance to be created.
function Keith() { this.sex = 'boy'; } var person = new Keith(); console.log(person.sex); //'boy'
In the above code, the Keith constructor is defined in the global scope, and then the constructor is called and assigned to the person object instance.
Three basic requirements for constructor creation: capitalize the first letter of the function name; use the this keyword inside the constructor to point to the object instance to be generated; use the new keyword to call the constructor and return the object instance.
If you want to learn more about the relationship between the constructor and the new keyword, please go to this article.
2.3: Object methods
When a method of object A is assigned to object B, this in the method changes from pointing to object A to pointing to object B. So be especially careful. When assigning a method of an object to another object, the pointer of this will be changed.
var keith = { sex: 'boy', foo: function() { return this.sex; } }; var rascal = { sex: 'girl' }; rascal.foo = keith.foo; console.log(keith.foo()); //'boy' console.log(rascal.foo()); //'girl'
In the above code, keith's foo function is assigned to rascal, then the point of this changes from keith to rascal.
如果某个方法位于多层对象的内部,这时为了简化书写,把该方法赋值给一个变量,往往会得到不一样的结果。
var a = { b: { p: 'keith', c: function() { return this.p; } } }; var person = a.b.c; console.log(person()); //undefined
上面代码中,c是两层对象里面的一个方法。为求简便,将其赋值给全局变量person,结果调用时,this指向了顶层对象window。而在window中变量p默认值为undefined。
要解决这个问题,可以只将c所在的对象赋值给person变量,或者是直接调用。
var person = a.b; console.log(person.c()); //'keith' console.log(a.b.c()); //'keith'
3.使用注意点
3.1:避免多层嵌套this
当在闭包中使用多层this,则this都会指向window。
function keith() { console.log(this); return function() { return this; } } keith(); //window keith()(); //window
上面代码中,在一个函数中返回另外一个匿名函数是闭包的特点之一,可以看出,当在闭包中使用this对象都会指向全局作用域中的window对象。
如果在函数外包含一个对象,则内部this指向全局作用域,而外部this对象指向当前作用域。
var o = { f1: function() { console.log(this); (function() { console.log(this) })(); } }; o.f1(); //Object , Window
上面代码包含两层this,结果运行后,第一层指向当前对象,第二层指向全局对象。
实际执行的是如下代码。
function keith() { console.log(this); } var o = { f1: function() { console.log(this); var f2 = keith(); } }; o.f1(); //Object , Window
要实现多层this嵌套,有两种解决方法:
一是在第二层中改用一个指向外层this的变量。
var o = { f1: function() { console.log(this); var that = this; (function() { console.log(that); })(); } }; o.f1(); //Object , Object
上面代码中,定义了局部变量that,固定指向了外层的this,然后在内层中使用that,就不会发生this指向的改变。但是如果函数外部内有嵌套一个对象,this还是会指向全局。
二是Javascript中的严格模式。在严格模式下,如果内部函数的this指向了window对象,就会报错。
var a = { count: 0, fun: function() { 'use strict'; return this.count++; } } var f = a.fun; console.log(f()) //'TypeError: this is undefined'
上面代码中,fun方法使用严格模式声明。把a对象中的fun方法赋值给全局变量f,那么this此时指向window对象,在严格模式下,就会报错。如果函数外部没有嵌套一个对象,那么不会报错,而是会返回undefined。
3.2:避免数组处理方法中的this
数组的map和foreach方法,允许提供一个函数作为参数。这个函数内部不应该使用this。
var keith = { a: 'Hello', b: ['b1', 'b2'], c: function() { this.b.forEach(function(item) { console.log(this.a + ' ' + item); }) } }; keith.c(); //undefined b1 //undefined b2
上面代码中,forEach方法的回调函数中的this,其实指向的是window对象,因此取不到keith.a的值,同上也属于避免多层嵌套this。也就是说,内层的this不指向外部函数,而是指向顶层对象。
要解决这个方法,可以使用that变量来代替回调函数中的this。
var keith = { a: 'Hello', b: ['b1', 'b2'], c: function() { var that = this; this.b.forEach(function(item) { console.log(that.a + ' ' + item); }) } }; keith.c(); //Hello b1 //Hello b2
另外一种方法,就是让this做为forEach方法的第二个参数,来固定它的运行环境。
var keith = { a: 'Hello', b: ['b1', 'b2'], c: function() { this.b.forEach(function(item) { console.log(this.a + ' ' + item); }, this) } }; keith.c(); //Hello b1 //Hello b2
3.3:避免回调函数中的this
回调函数中的this往往会改变指向。
var o = { f: function() { console.log(this === o); } }; o.f(); // true;
上面代码中,调用o对象的f方法,返回true。
但是,如果将f方法指定给某个按钮的click事件,this的指向就变了。
$('button').on('click',o.f);
上面代码中,使用了jquery方法来获取button元素,并绑定click事件。点击按钮后控制台会显示false。原因是此时this不再指向o对象了,而是指向按钮的DOM对象,因为f方法是在按钮对象的环境中被调用的。
总结一下:
a:如果想要多层嵌套this关键字,最常用的解决方法就是使用that变量,固定指向外层的this,然后在内层中使用that变量。就不会发生内层this指向全局的问题。
b:如果在回调函数中使用this关键字,注意this的指向问题。