学习:https://blog.csdn.net/weixin_34000916/article/details/88581297
https://blog.csdn.net/lianjiuxiao/article/details/109839865

一、什么是this

this是在运行时基于函数的执行环境绑定的,它的上下文取决于函数调用时的各种条件。
当一个函数被调用时,会创建一个活动记录(有时候也称执行上下文)。这个记录会包含函数在哪里被调用,函数的调用方法、出入的参数等信息。this就是记录其中一个属性,会在函数执行的过程中用到。


this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁实际上this的最终指向的是那个调用它的对象。
#this的绑定与函数声明的位置没有声明的位置没有任何关系,只取决于函数的调用方式
#一般来说,谁最终调用了函数,那么它就是this的绑定对象。
那在全局下调用函数,this的绑定对象也就是全局对象。

二、this的指向哪几种

参考回答:

默认绑定:全局环境中,this默认绑定到window。
隐式绑定:一般地,被直接对象所包含的函数调用时,也称为方法调用,this隐式绑定到该直接对象。
隐式丢失:隐式丢失是指被隐式绑定的函数丢失绑定对象,从而默认绑定到window。显式绑定:通过call()、apply()、bind()方法把对象绑定到this上,叫做显式绑定。
new绑定:如果函数或者方法调用之前带有关键字new,它就构成构造函数调用。对于this绑定来说,称为new绑定。
- 在全局执行上下文中,this 的值指向全局对象。(在浏览器中,this引用 Window 对象)。
- 在函数执行上下文中,this 的值取决于该函数是如何被调用的。如果它被一个引用对象调用,那么 this 会被设置成那个对象,否则 this 的值被设置为全局对象或者 undefined(在严格模式下)。
- 当 new 一个新对象,this指向返回的那个对象。
- 箭头函数里的 this 在函数定义时指定,默认指向定义它时所处的对象。


【1】构造函数通常不使用return关键字,它们通常初始化新对象,当构造函数的函数体执行完毕时,它会显式返回。在这种情况下,构造函数调用表达式的计算结果就是这个新对象的值。
【2】如果构造函数使用return语句但没有指定返回值,或者返回一个原始值,那么这时将忽略返回值,同时使用这个新对象作为调用结果。
【3】如果构造函数显式地使用return语句返回一个对象,那么调用表达式的值就是这个对象。

1、默认绑定到window

1
2
3
4
5
6
7
8
9
function a(){
var user = "追梦子";
console.log(this.user); // undefined
console.log(this); // Window
}
a(); // undefined 返回值
// this最终指向的是调用它的对象,这里的函数a实际是被Window对象所点出来的,下面的代码就可以证明。
window.a();
// 和上面代码一样,其实alert也是window的一个属性,也是window点出来的。

2、对象内方法调用,绑定该对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var o = {
user:"追梦子",
fn:function(){
console.log(this.user); //追梦子
}
}
o.fn();
// 这里的this指向的是对象o,因为你调用这个fn是通过o.fn()执行的,那自然指向就是对象o
window.o.fn();
// 指向o,打印信息一样

var o = {
a:10,
b:{
a:12,
fn:function(){
console.log(this.a); //12,指向父层b,只会是上一级,b如果没有a,这里打印undefined
console.log(this); // 返回b
}
}
}
o.b.fn();


情况1:如果一个函数中有this,但是它没有被上一级的对象所调用,那么this指向的就是window,这里需要说明的是在js的严格版中this指向的不是window,但是我们这里不探讨严格版的问题,你想了解可以自行上网查找。
情况2:如果一个函数中有this,这个函数有被上一级的对象所调用,那么this指向的就是上一级的对象。
情况3:如果一个函数中有this,这个函数中包含多个对象,尽管这个函数是被最外层的对象所调用,this指向的也只是它上一级的对象
**
注意:

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

// this永远指向的是最后调用它的对象,也就是看它执行的时候是谁调用的,
// 虽然函数fn是被对象b所引用,但是在将fn赋值给变量j的时候并没有执行所以最终指向的是window。
// 和上面直接执行的指向不一样

3、构造函数调用-new

1
2
3
4
5
6
7
8
function Fn(){
this.user = "追梦子";
}
var a = new Fn();
console.log(a.user); //追梦子
// 这里之所以对象a可以点出函数Fn里面的user是因为new关键字可以改变this的指向,将这个this指向对象a,
// 因为用了new关键字就是创建一个对象实例,调用这个函数Fn的是对象a,那么this指向的自然是对象a。
// 那么为什么对象a中会有user,因为你已经复制了一份Fn函数到对象a中,用了new关键字就等同于复制了一份。

注意:
如果返回值是一个对象,那么this指向的就是那个返回的对象,如果返回值不是一个对象那么this还是指向函数的实例。
还有一点就是虽然null也是对象,但是在这里this还是指向那个函数的实例,因为null比较特殊。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
1:
function fn()
{
this.user = '追梦子';
return {};
}
var a = new fn;
console.log(a.user); //undefined
2:
function fn()
{
this.user = '追梦子';
return function(){};
}
var a = new fn;
console.log(a.user); //undefined
3:
function fn()
{
this.user = '追梦子';
return 1;
}
var a = new fn;
console.log(a.user); //追梦子
4:
function fn()
{
this.user = '追梦子';
return undefined;
}
var a = new fn;
console.log(a.user); //追梦子
5:
function fn()
{
this.user = '追梦子';
return null;
}
var a = new fn;
console.log(a.user); //追梦子

三、箭头函数

  • 首先箭头函数是没有this的 ,它的作用域是和父级的上下文绑定在一起的,内部的this就是定义时上层作用域中的this。即它的this就是上一层第一个包裹它的普通函数的this。
  • this定义时绑定。也就是说,箭头函数内部的this指向是固定的,this总是指向函数定义生效时所在的对象。
  • 由于箭头函数没有自己的this,所以当然也就不能用call()、apply()、bind()这些方法去改变this的指向,是没有效果的。
  • 正是因为它没有this,所以也就不能用作构造函数。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    var name = 'window'
    const obj1 = {
    name: 'obj1',
    fn() {
    this.name = 'fn'
    inFn = () => {
    console.log(this.name)
    }
    inFn()
    }

    }

    obj1.fn()//'fn'
    obj1.fn.call(window)//'fn'
  1. 箭头函数的this指向,是父级程序的this指向
  2. 如果父级程序有this指向,指向向的就是父级程序的this指向
  3. 如果父级程序没有this指向(对象,数组是没有this),指向的是window
    1
    2
    3
    4
    5
    6
    7
    8
    9
    var myObject = { 
    a:'myObject.a',
    foo: ()=>{
    console.log(this.a)
    }
    };
    var a = 'window.a'

    myObject.foo() // 'window.a'

    四、定时器对this的影响

    setTimeout()调用的代码运行在与所在函数完全分离的执行环境上。这会导致,这些代码中包含的 this 关键字在非严格模式会指向 window (或全局)对象,严格模式下为 undefined,这和所期望的this的值是不一样的。
    备注:在严格模式下,setTimeout( )的回调函数里面的this仍然默认指向window对象, 并不是undefined
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    this.name = 'window'
    function Foo() {
    this.name = 'Foo'
    this.fn = function () {
    console.log(this.name)
    }
    this.timer = function () {
    setTimeout(this.fn)
    }
    }
    var obj = new Foo()

    obj.timer() //'window'

    解决setTimeout中的this指向问题

    当在vue中使用定时器来修改一个变量值的时候,发现没有效果,这是由于setTimeout函数调用的代码运行在与所在函数完全分离的执行环境上,这会使得this指向的是window对象。
    要想setTimeout指向正确的值,可以使用如下方法:

    1、可以使用call/apply/bind调用改变this绑定对象。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    this.name = 'window'
    function Foo() {
    this.name = 'Foo'
    this.fn = function () {
    console.log(this.name)
    }
    this.timer = function () {
    setTimeout(this.fn.call(this))
    }
    }
    var obj = new Foo()

    obj.timer() //'Foo'

    2、使用箭头函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    const obj = {
    name: 'obj',
    fn() {
    console.log(1)
    setTimeout(() => {
    console.log(this.name)
    }, 1000)
    }
    }

    obj.fn()// 1 'obj'
    此时函数的this指向的是定义它的时候的对象,也就是this指向了data内中对应的变量。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    export default {
    data () {
    return {
    left: -9999,
    bottom: -9999
    }
    },
    methods: {
    cancelMask: function () {
    setTimeout(() => {
    this.bottom = 0;
    this.left = 0;
    }, 500);
    }
    }
    }

3、将当前对象的this保存为一个变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
export default {
data () {
return {
left: -9999,
bottom: -9999
}
},
methods: {
cancelMask: function () {
var that = this;
setTimeout(function () {
that.bottom = 0;
that.left = 0;
}, 500);
}
}
}
----
this.name = 'window'
function Foo() {
var that = this
this.name = 'Foo'
this.fn = function () {
console.log(that.name)
}
this.timer = function () {
setTimeout(this.fn)
}
}
var obj = new Foo()

obj.timer() //'Foo'

方法中将this存在一个对象中,此时执行setTimeout函数时,setTimeout函数内的that就会访问到这个变量,就会得到当前对象。