> For the complete documentation index, see [llms.txt](https://sherwinshen.gitbook.io/qian-duan-xue-xi/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://sherwinshen.gitbook.io/qian-duan-xue-xi/frontend-basic/javascript/js-topics/this.md).

# this 的指向

不管是什么场合，`this`都有一个共同点：它总是返回一个对象。javascript 的 this 不是在函数定义的时候确定的，而是在调用的时候确定的，指向调用者。

```javascript
const A = {
  name: '张三',
  describe: function () {
    return '姓名：'+ this.name;
  }
};

const name = '李四';
const f = A.describe;
f() // "姓名：李四" - 相当于 window 调用了
```

![](/files/-M_iZdl3txiCEzfVLpcV)

## 1. 方法调用

通过对象来调用其方法函数 `对象.方法函数(...)`，函数中的 `this` 指向调用该方法的对象。

```javascript
const obj = {
    // 第一种方式，定义对象的时候定义其方法
    test() {
        console.log(this === obj);
    }
};

// 第二种方式，对象定义好之后为其附加一个方法(函数表达式)
obj.test2 = function() {
    console.log(this === obj);
};

// 第三种方式和第二种方式原理相同,是对象定义好之后为其附加一个方法(函数定义)
function t() {
    console.log(this === obj);
}
obj.test3 = t;

// 这也是为对象附加一个方法函数,但是这个函数绑定了一个不是 obj 的其它对象
obj.test4 = (function() {
    console.log(this === obj);
}).bind({});

obj.test(); // true  
obj.test2(); // true   
obj.test3(); // true    
obj.test4(); // false -受 bind() 影响，test4 中的 this 指向不是 obj
```

## 2. new 实例调用

在 es5 中，用 `new` 调用一个构造函数会创建一个新对象，而其中的 `this` 就指向这个新对象。

```javascript
var data = "Hi";    // 全局变量

function AClass(data) {
    this.data = data;
}

var a = new AClass("Hello World");
console.log(a.data);    // Hello World
console.log(data);      // Hi

var b = new AClass("Hello World");
console.log(a === b);   // false
```

但是构造函数如果有 return 语句，且 return 语句是个对象，则 new 出来的新对象是指 return 的这个对象，如果 return 的是非对象数据类型，则 this 依旧指向 new 的新对象。

```javascript
// 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); //追梦子
```

## 3. call/bind/apply 绑定

### 3.1 call()

函数实例的 `call` 方法，可以指定函数内部 `this` 的指向（即函数执行时所在的作用域），然后在所指定的作用域中，调用该函数。`call` 方法的参数，应该是一个对象。如果参数为空、`null` 和 `undefined`，则默认传入全局对象。

```javascript
// func.call(thisValue, arg1, arg2, ...)
// 第一个参数就是this所要指向的那个对象
​
var n = 123;
var obj = { n: 456 };
​
function a() {
  console.log(this.n);
}
​
a.call() // 123
a.call(null) // 123
a.call(undefined) // 123
a.call(window) // 123
a.call(obj) // 456
```

### 3.2 apply()

`apply` 方法的作用与 `call` 方法类似，也是改变 `this` 指向，然后再调用该函数。唯一的区别就是，它接收一个数组作为函数执行时的参数。

```javascript
// 1、找出数组最大元素
var a = [10, 2, 4, 15, 9];
Math.max.apply(null,a) // 15
​
// 2、将数组的空元素变为undefined，空与undefined的区别在于遍历时空会被跳过
Array.apply(null, ['a', ,'b']) // [ 'a', undefined, 'b' ]
​
// 3、转换类似数组的对象，比如arguments对象
Array.prototype.slice.apply({0: 1, length: 1}) // [1]
Array.prototype.slice.apply({0: 1}) // []
Array.prototype.slice.apply({0: 1, length: 2}) // [1, undefined]
Array.prototype.slice.apply({length: 1}) // [undefined]
​
// 4、绑定回调函数的对象
var o = new Object();
o.f = function () {
  console.log(this === o);
}
var f = function (){
  o.f.apply(o); // 或者 o.f.call(o);
};
$('#button').on('click', f);
```

### 3.3 bind()

`bind`方法用于将函数体内的 `this`绑定到某个对象，然后返回一个新函数。

```javascript
var counter = {
  count: 0,
  inc: function () {
    this.count++;
  }
};
​
var func = counter.inc.bind(counter);
func();
counter.count // 1
```

### 3.4 总结

| 类型    | 调用方式   | 第一个参数        | 其余参数    |
| ----- | ------ | ------------ | ------- |
| bind  | 返回一个函数 | this指向改变后的对象 | 直接传递    |
| apply | 函数直接调用 | this指向改变后的对象 | 用数组包裹参数 |
| call  | 函数直接调用 | this指向改变后的对象 | 直接传递    |

{% hint style="warning" %}
如果第一个参数为null或undefined，this 的指向就是全局变量，在浏览器里就是window对象。
{% endhint %}

{% hint style="warning" %}
不管我们给函数 bind 几次，fn 中的 this 永远由第一次 bind 决定。
{% endhint %}

## 4. 箭头函数中的 this

箭头函数中使用的 `this`，其实是直接包含它的那个函数或函数表达式中的 `this`。函数体内的`this`对象，是定义时所在的对象，而不是使用时所在的对象。或者可以说 箭头函数没有 this。

```javascript
const obj = {
    test() {
        const arrow = () => {
            // 这里的 this 是 test() 中的 this，
            // 由 test() 的调用方式决定
            console.log(this === obj);
        };
        arrow();
    },

    getArrow() {
        return () => {
            // 这里的 this 是 getArrow() 中的 this，
            // 由 getArrow() 的调用方式决定
            console.log(this === obj);
        };
    }
};

obj.test();     // true

const arrow = obj.getArrow();
arrow();        // true
```

## 5. 总结

this 代表函数调用相关联的对象，通常页称之为执行上下文。

1. 作为函数直接调用，非严格模式下，this指向window，严格模式下，this指向undefined
2. 作为某对象的方法调用，this通常指向调用的对象
3. 使用apply、call、bind 可以绑定this的指向
4. 在构造函数中，new 调用时指的是被构造的对象
5. 箭头函数没有单独的this值，this在箭头函数创建时确定，它与声明所在的上下文相同

简单来说就是：

* 普通函数的this ==> 谁调用就是谁（经常变：谁调用是谁）
* 箭头函数的this ==> 在谁的环境下`this`就是谁（不变：当前作用域），例如常见document/window等

{% hint style="info" %}
如果你对内容有任何疑问，欢迎提交 [❕issues](https://github.com/MrEnvision/Front-end_learning_notes/issues) 或 [✉️ email](mailto:EnvisionShen@gmail.com)
{% endhint %}
