this 指向全解:五条绑定规则、优先级与箭头函数的例外

举报
yd_252100366 发表于 2026/10/15 08:35:43 2026/10/15
【摘要】 ## 一、核心认知:this 是调用时决定的this 不取决于函数写在哪,取决于**函数怎么被调用**。同一个函数,赋值给不同对象再调用,this 完全不同:```jsfunction who() {  return this.name;}const a = { name: 'A', who };const b = { name: 'B', who };who();          // ...

## 一、核心认知:this 是调用时决定的

this 不取决于函数写在哪,取决于**函数怎么被调用**。同一个函数,赋值给不同对象再调用,this 完全不同:

```js
function who() {
  return this.name;
}
const a = { name: 'A', who };
const b = { name: 'B', who };

who();          // undefined(严格模式)或 window.name(非严格)
a.who();        // 'A'
b.who();        // 'B'
```

判断 this 只需要问一个问题:**这个函数最终是谁调的?**

## 二、五条绑定规则(按优先级从高到低)

### 1. new 绑定:this 指向新创建的对象

```js
function Person(name) {
  this.name = name;
}
const p = new Person('Tom');
p.name; // 'Tom'
```

new 的执行过程:创建空对象 → 原型挂到对象上 → this 指向该对象执行构造函数 → 返回该对象。

### 2. 显式绑定:call / apply / bind

```js
fn.call(obj, 1, 2);      // 参数逐个传
fn.apply(obj, [1, 2]);   // 参数数组传
const bound = fn.bind(obj, 1); // 永久绑定,返回新函数
```

注意 bind 的绑定是**不可覆盖**的(除了 new),且对原始值参数会自动装箱:`fn.call(5)` 里 this 是 `Number {5}`。

### 3. 隐式绑定:this 指向最后调用它的对象

```js
const obj = {
  name: 'obj',
  greet() {
    return this.name;
  },
};
obj.greet(); // 'obj'
```

注意"最后"二字——链式调用 `a.b.c()` 时,this 是 `c` 所在的 `a.b`,不是 `a`。

### 4. 默认绑定:没人调就指向全局

```js
function plain() {
  return this;
}
plain(); // window(浏览器)/ globalThis(Node)/ undefined(严格模式)
```

### 5. 箭头函数:沿用外层 this

箭头函数没有自己的 this,定义时所在的词法作用域是谁,this 就是谁,且 call/apply/bind 改不动:

```js
const obj = {
  name: 'obj',
  normal() {
    return this.name; // 'obj'
  },
  arrow: () => this.name, // 外层作用域的 this,通常是 undefined
};
```

## 三、隐式绑定丢失:最高频的 bug

最常见的翻车是把方法当回调传出去,调用者变成了别的对象:

```js
const button = {
  label: '提交',
  handleClick() {
    console.log(this.label); // 期望 '提交'
  },
};

setTimeout(button.handleClick, 100); // 输出 undefined!
```

`button.handleClick` 取出的是函数本身,setTimeout 调用时 this 是 window/undefined。三种修法:包一层箭头 `() => button.handleClick()`、`bind(button)`、或直接用箭头函数定义方法。

## 四、手写 call / apply / bind

面试常考,核心思路都是"借对象的属性临时挂载再调用":

```js
Function.prototype.myCall = function (context, ...args) {
  context = context || globalThis;
  const key = Symbol('fn'); // 防止覆盖原有属性
  context[key] = this;
  const result = context[key](...args);
  delete context[key];
  return result;
};

Function.prototype.myBind = function (context, ...args) {
  const fn = this;
  return function bound(...newArgs) {
    // new 调用时 this 指向实例,绑定失效
    if (this instanceof bound) {
      return new fn(...args, ...newArgs);
    }
    return fn.apply(context, args.concat(newArgs));
  };
};
```

两个细节:用 Symbol 做临时 key 避免污染;bind 版本必须处理 new 覆盖绑定的情况。

## 五、实践中的三条军规

1. **React class 组件的方法必须 bind**:`this.handleClick = this.handleClick.bind(this)`,或者干脆用 class property 箭头函数 `handleClick = () => {}`。
2. **Vue 的 methods 不用管**:编译时已自动绑定到组件实例,直接写普通方法即可。
3. **回调优先用箭头函数**:数组方法(map/filter/forEach)的回调写箭头,this 自动沿外层,省掉一层 bind。

## 六、总结

判断 this 的顺序:看是否被 new → 是否被 call/apply/bind → 是否被对象调用 → 都没有就是默认绑定;箭头函数全程例外,直接看外层作用域。把这个顺序背熟,90% 的 this 问题都能当场定位。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。