疯了!js中this到底指向什么?

一、前言

前段时间,公司让我改一个界面,我心想改个界面还不简单吗?结果呃,我低估了这颗炸弹的威力。

好吧是我太菜,总结一下,这个this的指向问题。

二、测试例子

首先说明一点。在js中,this的指向在定义函数的时候是确定不了的,只有在使用这个函数的时候才能确定this的指向。

一般来说在使用时,谁调用的这个函数,函数中的this就指向它。

2.1)普通函数

1
2
3
4
5
6
7
8
9
10
11
var username = "BANMOON";
let age = 18;
function showInfo(){
console.log(username);// BANMOON
console.log(this.username);// BANMOON

console.log(age);// 18
console.log(this.age);// undefined
}
showInfo();
// window.showInfo();

image-20220228170121758

这里还涉及到varlet的区别,

  • let的作用域是在当前的代码块中,且它挂载的对象是script,而不是window

  • var的作用域是在全局,且挂载与window

由于上面两个区别,所以可以看到直接运行普通函数,其中的this是指向window的。

你可以把11行放开,这是一样的效果

2.2)对象函数

1
2
3
4
5
6
7
8
let me = {
username: "BANMOON",
age: 18,
showName: function(){
console.log(this.username);
}
}
me.showName();// BANMOON

image-20220228171047113

好的,和上面的呈现了不一样的效果。对象中的函数,在运行时this指向了me这个对象。

我们可以再加点料

1
2
var showName = me.showName;
showName();// undefined

image-20220228171425765

这次又变成了undefined,我们现在再回去看看刚刚解释的那句话。

在js中,this的指向在定义函数的时候是确定不了的,只有在使用这个函数的时候才能确定this的指向。

这样,我稍微能理解点了,简单的来说就是谁调用的这个函数,函数里面的this就指向谁。

image-20220228172502346

如果真的这么简单就好了,别急还有

2.3)构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var Person = function(name, age){
this.name = name;
this.age = age;

this.getName = function(){
return this.name;
}

this.getFirendName = function(){
return this.firend.getName();
}
}

let me = new Person("BANMOON", 18)
me.firend = new Person("阿超", 18);

console.log(`我的名字:${me.getName()}`);// BANMOON
console.log(`朋友的名字:${me.getFirendName()}`);// 阿超

image-20220228173338770

这里没有什么好说的,同样查看谁起调的函数就好了。谁调用的那个函数,函数中的this就指向它

1
2
3
4
5
6
7
8
9
var User = function(username){
this.username = username;
this.getName = function(){
return this.name;
}
return new Person("阿超", 18);
}
let user = new User("BANMOON");
console.log(user.getName());// 阿超

image-20220228173805575

这段代码,为什么又会变成阿超呢?主要是这段函数出现了返回值,它把Person对象返回了。

所以在外部看来,user是User这个函数创建的一个对象,但实际上已经被掉包成了Person的一个对象。所以这时候,this的真正指向就是返回的那个对象。

2.4)setTimeout,apply等方法

1
2
3
4
5
6
7
8
9
10
var username = "九月";
let me = {
username: "BANMOON",
showName: function(){
console.log(this.username);
}
}
me.showName();// BANMOON

setTimeout(me.showName, 0);// undefined

image-20220228224723702

第八行大家都知道,适应上面的逻辑,没有什么问题。可就是这个第十行,怎么就输出了九月了呢。

为什么this会指向window,请注意第10行,传入的是一个函数,me.showName没有括号。如此,这段的解释就是,在window环境下启用这个定时器,将立刻执行me.showName这个函数。

由此来看,定时器启动的函数中的this都是指向与window。我们再来看这个

1
2
3
4
5
6
7
8
9
10
11
12
var username = "九月";
let me = {
username: "BANMOON",
showName: function(){
console.log(this.username);
},
showName2: function(){
setTimeout(this.showName, 0)
}
}
me.showName();// BANMOON
me.showName2();// 九月

image-20220228224723702

如果一定要使用定时器,又不想它指向window,那该怎么办呢?

applycallbind可以解决this指向的问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var username = "九月";
let me = {
username: "BANMOON",
showName: function(){
console.log(this.username);
},
}

let showName = me.showName;
setTimeout(function(){
showName.apply(me);
}, 0);// BANMOON

setTimeout(function(){
showName.call(me);
}, 0);// BANMOON

setTimeout(function(){
let show = showName.bind(me);
show();
}, 0);// BANMOON

image-20220228230640516

关于他们的具体使用,本文不做详述。只是将this想指向的对象作为上面函数的参数即可。

2.5)箭头函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var username = "阿超";
let me = {
username: "BANMOON",
showName: () => {
console.log(this.username);
},
}
me.showName();// 阿超

let aother = {
username: "BANMOON",
showName: function(){
console.log(this.username);
}
}
aother.showName();// BANMOON

image-20220228231715405

下面那个很好理解,this指向的就是aother

那么上面的箭头函数该如何理解呢。其实是这样,箭头函数没有this,它的this来源于上一个,是继承于外面的环境。

所以me.showName()调用时,me不再生效,this将指向于window

你说箭头函数套娃?那么遇到一个往上找一个环境,直到不是箭头函数为止。

三、结语

上面的this指向,网上很有多,但我平常没有在意。用到的时候,发现储备不足。

就这样吧,我整理了一下,希望可以帮到自己。

我是半月,祝你们幸福!!!