请问这this是那个?-- this指向理清

5.28日更新,最近发现ES6的尖头函数中的this有点巧妙,不适用于以下三条规则,在后面补充下


开头放结论

一个函数如果被调用或者执行了,那么函数中的this指向:

  • 一般情况下指向执行时调用其的对象
  • 函数当作构造函数使用且函数内没有返回值,或返回值是5种基本型(Undefined类型、Null类型、Boolean类型、Number类型、String类型)之一,指向实例后的对象
  • 函数当作构造函数使用且有return值,返回是是数组啊、对象啊什么的,this指向返回值
    接下来稍微解释下

其实上面也不是全部情况(例如use strict等),但是基本囊括了所有情况,来一一举例下,第一种:

1
2
3
4
5
6
7
8
var normal = function () {
var a = '你大爷'
console.log(this);
console.log(this.a);
}
a()
// Unddefined
// Window对象

这种有一定隐蔽性,但是如果换个写法

1
2
3
window.a()
// Unddefined
// Window对象

会不会好理解点了呢,因为a其实是定义在window上的,所以它的执行或者说被调用自然也是window来动手,所以按照第一条指向执行时调用其的最近的对象,这里的this就指向window对象,而window上并没有a这个变量,自然是undefined

第二种:

1
2
3
4
5
6
7
8
9
10
var normal = {
a: '你大爷',
fn: function () {
console.log(this);
console.log(this.a);
}
}
normal.fn()
// normal
//你大爷

这里this在fn里面,而谁调用了fn呢?normal!所以this指向normal,normal.a就是你大爷

诶!这个时候可能你会说了,上面的代码不是和

1
window.normal.fn()

一样吗?那this为什么不是window而是nomal呢,让我们来补充下第一个结论

一般情况下指向执行时调用其的最近的对象

这样是不是会好理解点了呢?不急,我们再来看几个

1
2
3
4
5
6
7
8
9
10
11
12
var normal = {
a: '你大爷',
fn: function () {
console.log(this);
console.log(this.a);
}
}
// 不一样在这里
var haha = normal.fn
haha()
// window
// Undefined

是不是很奇怪?其实想一下haha()的执行其实是这样子的window.haha()就没问题了吧,但是为什么是这样呢? 我们这样说,normal.fn并没有执行其实是一个函数的引用所以haha其实也是个引用,所以当haha()执行了,调用它的还是window 。

接下来说构造函数的情况

1
2
3
4
5
6
var construction = function () {
this.a = '你大爷'
}
var me = new construction()
console.log(me.a )
// 你大爷

new会创建一个匿名对象,然后把构造函数里的this指向这个匿名对象,这里最后又把匿名对象赋值给me,有点给匿名对象赋予名字的意思,所以匿名对象有a属性,me自然也有了,不知道讲明白没

然后,如果构造函数有返回值而且返回的是一个数组或对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var construction = function () {
this.a = '你大爷';
return {a: '嘿嘿嘿'}
}
var me = new construction()
console.log(me.a)
// 嘿嘿嘿
var construction = function () {
this.a = '你大爷';
return [1,2,3,4]
}
var me = new construction()
console.log(me)
// [1,2,3,4]

这个时候其实new就像是并没有最终创建一个匿名对象,而是选择接受构造函数返回值然后再赋予实例,所以实例后的me会是构造函数的返回值,有点绕口啊~但是不是所有返回值都会产生这样的情况呢,答案是!就像开头说的,如果返回值是Undefined、Null、Boolean、Number、String之一,情况就会

1
2
3
4
5
6
7
var construction = function () {
this.a = '你大爷';
return '哈哈哈'
}
var me = new construction()
console.log(me.a)
// 你大爷

和没有返回值一样~总结差不多就结束了

最后说一下 apply, call, bind

这些方法会改变this指向的问题其实很明了,比如平常常用的把args转化为数组

1
[].prototype.slice.call(arguments)

想了想还是不赘述了,that’s all, 荆轲刺秦王

ES6 中的 箭头函数 中的this指向问题

ES6貌似为了解决之前this变化无常的问题就通过箭头函数带来的词法作用域来消除this的问题
总的来说就是词法作用域的this特性让变量的this总是指向词法申明时的那个对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function logThisID (id, callback) {
setTimeout(() => {
callback(1)
}, 0)
}
var person = {
name: 'wayne',
ID: '10086',
logThisName: function () {
console.log(this.name) // wayne
logThisID(this.id, (number) => {
console.log(this.ID) // 10086
console.log('number = ' + number) // 1
})
}
}

其实相当于是在logThisName里缓存了下this

1
2
3
4
5
6
7
8
9
10
11
12
13
var person = {
name: 'wayne',
ID: '10086',
logThisName: function () {
var _this = this
console.log(this.name) // wayne
logThisID(this.id, (number) => {
console.log(_this.ID) // 10086
console.log('number = ' + number) // 1
})
}
}

这些都只是暂时看到,还没深入去理解,以后回来补充

参考:追梦大大的 彻底理解js中this的指向