Javascript中的this指向

this的指向问题

初步了解this的指向

在JavaScript中,this的指向在函数定义时是无法确定的。只有在函数执行时才能确定。初步的
可以认为JavaScript中的this指向最后一个调用它的对象

例子1

1
2
3
4
5
6
function exp1() {
var user = 'phil';
console.log(this.user);//unfinded
console.log(this);//window
}
exp1()

在此例中exp1函数事实上是被window对象调用的,我们所创建的变量函数都是window的属性,因而this的指向是window

更进一步了解

但事实上也存在与上面例子不同的情况
例子2

1
2
3
4
5
6
7
var a = {
name:'phil',
fn: function () {
console.log(this.name);//phil
}
}
window.a.fn()

例子3

1
2
3
4
5
6
7
8
9
10
var b = {
b1: 'phil',
b2: {
b1: 'Pphil',
bb2: function () {
console.log(this.b1);//Pphil
}
}
}
b.b2.bb2()

通过以上三个例子可以看出,初步结论并不一定对。其实this的指向分为三种情况

  • 如果函数中this,但没有被上一级调用,那么this指向window(严格模式不算,因为严格模式会屏蔽this指向全局对象)
  • 如果一个函数中有this,这个函数有被上一级的对象所调用,那么this指向的就是上一级的对象。
  • 如果一个函数中有this,这个函数中包含多个对象,尽管这个函数是被最外层的对象所调用,this指向的也只是它上一级的对象

例子4

1
2
3
4
5
6
7
8
9
10
11
12
var o = {
a:10,
b:{
a:12,
fn:function(){
console.log(this.a); //undefined
console.log(this); //window
}
}
}
var j = o.b.fn;
j();

为什么这里又是window而不是上一级对象呢,因为需要看最后函数执行的时候是谁在调用它。这里将fn赋值给变量j的时候并没有执行,j是一个全局变量所以最后是windiow

构造函数中的this

例子5

1
2
3
4
5
6
function exp5(){
this.name = 'phil';
}

var p = new exp5();
console.log(p.name);//phil

这里最后调用的是p,p是全局对象但new关键字会改变this的指向

例子6

1
2
3
4
5
6
function fn() {
this.name = 'phil';
return {}
}
var q = new fn();
console.log(q.name);//unfinded

当return一个对象时,return则指向那个对象,不是对象(但包括null)则还是指向原来的函数

this与settime

改变this指向 call apply bind

call apply bind都可以改变this的指向,但都有各自不同的地方

call

call可以把参数的环境添加到调用的对想象中

1
2
3
4
5
6
7
8
9
var a =  {
name: 'phil',
skill: function () {
console.log(this.name);
}
}
var o = a.skill;
o()//unfinded
o.call(a)//phil

call也可以支持多个参数,用逗号分割

o.call(a,x,xx,xxx)

apply

apply与call功能相同,但是第二个参数必须以数组的形式

o.apply(a,[x,xx,xxx])

bind

bind并不会像call和apply那样直接执行而是返回一个函数

1
2
3
4
5
6
7
8
9
var a =  {
name: 'phil',
skill: function () {
console.log(this.name);
}
}
var o = a.skill;
var c = o.bind(a)
c()//phil

定时器中的this

匿名函数,定时器中的函数,由于没有默认的宿主对象,所以默认this指向window

1
2
3
4
5
6
7
8
var obj = {
say: function () {
setTimeout(function () {
console.log(this)//window
});
}
}
obj.say();

如果要在setTimeout/setInterval中使用this可以采用以下方法

  • 保存this

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    var obj = {
    func: function() {},
    say: function () {
    var that = this; //此时的this就是obj对象
    setTimeout(function () {
    console.log(this)
    that.func()
    });
    }
    }
    obj.say();
  • 使用bind函数给回调函数绑定宿主对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    var obj = {  
    func: function() {},
    say: function () {
    // 此时的this就是obj对象
    setTimeout(function () {
    console.log(this)
    this.func()
    }.bind(this));
    }
    }
    obj.say(); // obj

一道练习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
window.val = 1;
var obj = {
val: 2,
dbl: function () {
this.val *= 2;
val *= 2;
console.log(val);
console.log(this.val);
}
};

obj.dbl();
var func = obj.dbl;
func();//2488

第一次调用时this.val的指向时obj,val变量在没有指定对象前缀,默认从函数中找,找不到则从window中找全局变量
因此结果是

1
2
3
4
5
6
7
8
9
10
window.val = 1;
var obj = {
val: 2,
dbl: function () {
this.val *= 2;//2*2=4
val *= 2;//1*2
console.log(val);//2
console.log(this.val);//4
}
};

第二次调用时func是全局变量所以最后的调用对象是window,this指向window

1
2
3
4
5
6
7
8
9
var obj = {
val: 2,
dbl: function () {
this.val *= 2;//2*2=4
val *= 2;//4*2=8
console.log(val);//8
console.log(this.val);//8
}
};

es6箭头函数中的this

箭头函数中其实没有this,都是通过继承而来,严格模式下,没有宿主调用的函数中的this是undefined,所以箭头函数中的也是undefined。

1
2
3
4
5
6
7
8
var obj = {
say: function () {
setTimeout(() => {
console.log(this)
});
}
}
obj.say(); // obj

这个时候this继承自obj,所以指向obj