作为一个front-end engineer,我们时不时的会遭遇this的伏击,今天就让我们掀开this的神秘面纱吧
一、全局执行
console.log(this) // Window
可以看到,全局作用域中的this指向的是当前全局对象Window。
二、函数中执行
1.非严格模式下:
function func(){
console.log(this) // Window
}
func();
在非严格模式下,this指向Window
2.严格模式下:
'use strict'
function func(){
console.log(this) //undefined
}
func();
三、作为对象的的方法调用
var obj = {
name:'123',
func:function(){
console.log(this.name) // '123'
}
}
obj.func();
当一个函数被当作一个对象的方法调用的时候,this指向当前的对象obj
---
var obj = {
name:'222',
func:function(){
console.log(this) // Window
}
}
var t = obj.func;
t();
如果把对象的方法赋值给一个变量,调用该方法时,this指向Window。(我的理解为:t是全局变量,t = window.t,this遵循谁调用,就指向谁的原则,故而指向了Window)。
四、作为一个构造函数使用
1.在js中,为了实现类,我们需要定义一些构造函数,在调用一个构造函数的时候加上new这个关键字:
function Person(name){
this.name = name;
console.log(this); // Person
}
var p1 = new Person('lee');
此时,this指向这个构造函数调用的时候实例化出来的对象。
当然,构造函数其实也是一个函数,若将构造函数当作普通函数来调用,this指向Window:
function Person(name) {
this.name = name;
console.log(this); //Window
}
var p2 = Person('GG');
五、在定时器中使用
setInterval(function(){
console.log(this);
},2000)
setTimeout(function(){
console.log(this)
},0)
如果没有特殊的指向,setInterval和setTimeOut的回调函数中this的指向都是Window。这是因为JS的定时器方法是定义在Window下的。
六、箭头函数
var func = () => {
console.log(this) // window
}
var obj = {
name:'123',
fun:function(){
console.log(this)
}
}
obj.fun() // obj
普通函数作为对象的一个函数被调用,this指向obj,箭头函数作为对象的一个函数被调用,this指向Window.
关于箭头函数中的this指向:
var obj = {
name:'kk',
func:function(){
settimeout(function(){
console.log(this)
})
}
}
obj.func() //window
var obj = {
name:'hh',
func:function(){
setTimeout(()=>{
console.log(this)
},0)
}
}
obj.func() //obj
若在对象的函数中,普通函数作为定时器延时执行的函数调用,this 指向 Window;箭头函数作为定时器延时执行的函数调用, this 指向定义时所在的对象,也就是 func 中的 this,即 obj。
箭头函数中 this 的值取决于该函数外部非箭头函数的 this 的值,且不能通过 call() 、 apply() 和 bind() 方法来改变 this 的值。
七、call apply bind
call:
fun.call(this,arg)
它会立即执行函数,第一个参数是指定执行函数中this的上下文,后面的参数是执行函数需要传入的参数。
apply
fun.apply(this,[])
它也会立即执行函数,第一个参数是指定执行函数中this的上下文,第二个参数是一个数组,是传给执行函数的参数。
bind
function.bind(thisArg[, arg1[, arg2[, ...]]])
它不会执行函数,而是返回一个新的函数,这个新的函数被指定了 this 的上下文,后面的参数是执行函数需要传入的参数。
示例:
function Person(name,age){
this.name = name;
this.age = age;
console.log(this);
}
var obj = {
name:'kk',
age:6
}
Person.call(obj,'mm',10);
// obj,{name:'mm',10};
Person.apply(obj,['mm',10]);
// obj,{name:'mm',10};
var p1 = Person.bind(obj,'mm',10)
var p2 = new p1();
//Person {name:'mm',age:10}
在此例中,this都指向了obj,都正常运行,call、apply会立即执行,call和apply的区别在于参数的传递。call接受多个参数,apply参数只接受一个数组;bind不是立即执行函数,它返回一个函数,需要执行p2才能返回结果,bind接收多个参数列表。
如何改变this的指向
1.使用es6的箭头函数
var name = 'hh';
var obj = {
name:'kk',
func1:function(){
console.log(this.name)
},
func2:function(){
setTimeout(function(){
this.func1();
},1000)
}
}
obj.func1();//kk
obj.func2()//Uncaught TypeError: this.func1 is not a function
obj.func1中this指向调用此函数的obj,所以this.name是kk
obj.func2中的setTimeout是window下的settimeout,故而settimeout中的this指向的是window,全局中没有func1这样函数,故而报错。
var name = 'hh';
var obj = {
name:'kk',
func1:function(){
console.log(this.name)
},
func2:function(){
setTimeout(function(){
this.func1();
},1000)
}
}
obj.func1();//kk
obj.func2()// kk
这时候,没有报错,因为箭头函数的this的值取决于该函数外部非箭头函数的this的值,也就是func2的this的值,即obj。
2.在函数内部使用 _this = this
var name = 'hh';
var obj = {
name:'kk',
func1:function(){
console.log(this.name)
},
func2:function(){
let _this = this
setTimeout(function(){
_this.func1();
},1000)
}
}
obj.func1();//kk
obj.func2() //kk
func2也可以正常运行,在func2中,首先设置var _this = this, 这里的this指向func2的对象obj,为了防止在func2的setTimeout 被window调用而导致的在setTimeot中的this为window,我们将this(指向变量obj)赋值给一个变量_this,这样,在func2中我们使用的_this就是指向对象obj了。
3.使用call、apply、bind
call:
var name = "hh";
var obj = {
name:'kk',
func1: function (){
console.log(this.name)
},
func2:function(){
setTimeout(function(){
this.func1();
}.call(obj),1000)
}
},
obj.func2();// kk
apply:
var name = 'hh';
var obj = {
name:'kk',
func1:function(){
console.log(this.name)
},
func2:function(){
setTimeout(function(){
this.func1();
}.apply(obj),1000)
}
}
obj.func2() //kk
bind:
var name = 'hh';
var obj = {
name:'kk',
func1:function(){
console.log(this.name)
},
func2:function(){
setTimeout(function(){
this.func1();
}.bind(obj)(),1000)
}
}
obj.func2() //kk
call,apply,bind都能改变this的上下文对象,所以也没有报错,可以正常执行。