首先要搞懂一个问题,函数中的this到底是怎么来的?
普通函数参数列表中有一个默认参数self,这个self的值就是根据该函数被调用时所处的上下文来定的,需要说明的一点是,变量存储的都是地址:
每个function(){}都是独立存在的,就算赋值给一个变量a,该变量a也只是持有该函数的地址,这时候如果再将该变量a赋值给变量b,这时候b就直接持有function(){}的地址,调用b()时候就触发了函数的执行,这时候就会传入上下文当第一个参数(隐藏),所以最终传进去的self的值就是该函数执行环境的上下文。
var obj = {
a: function () { console.log(this.bar) },
bar: 1
};
var b = obj.a;
var bar = 2;
//在obj对象内执行,上下文为obj对象。
obj.a() // 1
//上下文为window对象。
b() // 2
注意:箭头函数没有默认参数self,运行的时候也就不会有上下文传进去。
搞懂了this是怎么来的,我们再来看问题:函数被调用时,函数内的this指向哪个对象?
我们把函数分为普通函数与箭头函数2种
普通函数下:
1.this总是指向它的直接调用者(使用时所在的上下文)。
例如:obj.func ,那么func中的this就是obj,在window中直接调用func(),那么func中的this就是window箭头函数下:
1.箭头函数的this对象,就是定义时所在的上下文(即定义时的上层函数内的this),而不是使用时所在的上下文。
因为箭头函数没有默认参数self,所以箭头函数内的this在定义的时候就已经被赋值了this = 定义时所在上下文。
下面举个例子验证下:
let obj={
foo:()=>{
console.log(this) //1. window
return () => {
console.log(this) //2. window
return function(){
console.log(this) //3. 定义时所在的上下文,这里指上层函数的this
return () => {
console.log(this) //4. 定义时所在的上下文,这里指上层函数的this
}
}
};
}
}
//这种情况第四个this都指向window,因为调用它的对象为window
var b = obj.foo().call({name: 'chenyou'})() ()
//这种情况第三个this指向{name: 'chenyou'},因为调用它的是这个对象
//第四个this也指向{name: 'chenyou'},因为它定义在第三个方法内,而第三个方法内的this指向{name: 'chenyou'}
var b = obj.foo()().call({name: 'chenyou'})()
思考:第一个箭头函数是定义在obj对象中的,根据定义应该指向obj对象才对,为什么第一个箭头函数的this指向了window呢?
要回答这个问题就涉及到Window的原理了,Window就是一个函数,在控制台打印Window :
ƒ Window() { [native code] }
再打印Window的实例window:
一堆可供调用的方法…
__ proto__:Window
看到这里我们发现window对象与我们的普通对象基本无区别,
既然这样我们就自己造一个假Window放在真Window中:
function myWindow(){
this.test = {
getThis: this //该this指向myWindow中的上下文this
}
this.test.obj = {
name: 'chenyou',
getThis:this,
foo: () => {
console.log(this)
}
}
this.test.obj.foo() //this指向myWindow中的this
}
var myw = new myWindow()
myw.test.getThis // myWindow
可以看到普通对象中的this确实是指向的外层function(){}中的上下文。
箭头函数中的this指向了myWindow中的上下文(this),而这个上下文取决于调用myWindow这个函数传进来的self参数。
从上面这两个举例我们暂时得出结论(经过实验证明结论是正确的):
结论1:只要箭头函数定义的地方的外层有普通函数,不管嵌套了几层普通对象,箭头函数中的this都是指向最近外层的普通函数中的this.
突然想到new一个对象,给对象赋值箭头函数,看看箭头函数中的this指向谁
var myclass = function () {
this.name = 'chenyou'
this.foo = () => {
console.log(this)
}
}
myclass.prototype.pfoo = function(){
console.log(this)
}
myclass.prototype.pjfoo = () => {
console.log(this)
}
var myc = new myclass()
myc.foo() // myclass
myc.pfoo() //myclass
myc.pjfoo () //Window
myc.foo2 = () => {
console.log(this)
}
myc.foo3 = function(){
console.log(this)
}
myc.foo2() // Window
myc.foo3.call({name:"hhh"})
foo()箭头函数因为是定义在myclass函数中的,所以this指向myclass的实例
pfoo()普通函数因为是在Window下调用,所以this指向Window
pjfoo()、foo2()箭头函数因为是在Window下定义,所以this指向Window
foo3()普通函数因为调用它的对象是{name:”hhh”},所以指向{name:”hhh”}对象
这里foo与foo2中的this不同是因为它们之间隔了个new。
结论2:箭头函数在哪定义,不管赋值给了哪个对象的变量,不管嵌套了几层普通对象,this都指向都是它最近外层函数中的this