前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JS 中几种轻松处理’this’指向方式

JS 中几种轻松处理’this’指向方式

作者头像
Javanx
发布2019-09-30 10:04:51
9890
发布2019-09-30 10:04:51
举报
文章被收录于专栏:web秀web秀

我喜欢在JS中更改函数执行上下文的指向,也称为 this 指向。

例如,咱们可以在类数组对象上使用数组方法:

代码语言:javascript
复制
const reduce = Array.prototype.reduce;

function sumArgs() {
   return reduce.call(arguments, (sum, value) => {
         return sum += value;
  });
}

 sumArgs(1, 2, 3); // => 6
 ```

另一方面,`this` 很难把握。

咱们经常会发现自己用的 `this` 指向不正确。下面的教你如何简单地将 `this` 绑定到所需的值。

在开始之前,我需要一个辅助函数`execute(func)`,它仅执行作为参数提供的函数。

```JavaScript
function execute(func) {
    return func();
}

 execute(function() { return 10 }); // => 10
 ```

现在,继续理解围绕`this`错误的本质:方法分离。

## 1.方法分离问题
假设有一个类`Person`包含字段`firstName`和`lastName`。此外,它还有一个方法`getFullName()`,该方法返回此人的全名。如下所示:

```JavaScript
function Person(firstName, lastName) {
    this.firstName = firstName;
  this.lastName = lastName;

  this.getFullName = function() {
     this === agent; // => true
         return `${this.firstName} ${this.lastName}`;
  }
}

const agent = new Person('前端', '小智');
 agent.getFullName(); // => '前端 小智'
 ```

可以看到`Person`函数作为构造函数被调用:`new Person('前端', '小智')`。 函数内部的 `this` 表示新创建的实例。

`getfullname()`返回此人的全名:`'前端 小智'`。正如预期的那样,`getFullName()`方法内的 `this` 等于`agent`。

如果辅助函数执行`agent.getFullName`方法会发生什么:

```JavaScript
 execute(agent.getFullName); // => 'undefined undefined'
 ```

执行结果不正确:`'undefined undefined'`,这是 `this` 指向不正确导致的问题。

现在在`getFullName()` 方法中,`this`的值是全局对象(浏览器环境中的 `window` )。 `this` 等于 `window`,`${window.firstName} ${window.lastName}` 执行结果是 `'undefined undefined'`。

发生这种情况是因为在调用`execute(agent.getFullName)`时该方法与对象分离。 基本上发生的只是常规函数调用(不是方法调用):

```JavaScript
 execute(agent.getFullName); // => 'undefined undefined'

// 等价于:

const getFullNameSeparated = agent.getFullName;
 execute(getFullNameSeparated); // => 'undefined undefined'
 ```

这个就是所谓的方法从它的对象中分离出来,当方法被分离,然后执行时,`this` 与原始对象没有连接。

为了确保方法内部的`this`指向正确的对象,必须这样做

1. 以属性访问器的形式执行方法:`agent.getFullName()`
2. 或者静态地将`this`绑定到包含的对象(使用箭头函数、`.bind()`方法等)

方法分离问题,以及由此导致`this`指向不正确,一般会在下面的几种情况中出现:

**回调**

```JavaScript
// `methodHandler()`中的`this`是全局对象
setTimeout(object.handlerMethod, 1000);

在设置事件处理程序时

代码语言:javascript
复制
// React: `methodHandler()`中的`this`是全局对象
<button onClick={object.handlerMethod}>
  Click me
</button>

接着介绍一些有用的方法,即如果方法与对象分离,如何使this指向所需的对象。

2. 关闭上下文

保持this指向类实例的最简单方法是使用一个额外的变量self:

代码语言:javascript
复制
```JavaScript
 function Person(firstName, lastName) {
     this.firstName = firstName;
   this.lastName = lastName;
const self = this;
this.getFullName = function() {
      self === agent; // => true
          return <code>${self.firstName} ${self.lastName}</code>;
   }
 }
const agent = new Person('前端', '小智');
agent.getFullName();        // => '前端 小智'
  execute(agent.getFullName); // => '前端 小智'
  ```

getFullName()静态地关闭self变量,有效地对this进行手动绑定。

现在,当调用execute(agent.getFullName)时,一切工作正常,因为getFullName()方法内 this 总是指向正确的值。

3.使用箭头函数

有没有办法在没有附加变量的情况下静态绑定this? 是的,这正是箭头函数的作用。

使用箭头函数重构Person:

代码语言:javascript
复制
```JavaScript
 function Person(firstName, lastName) {
     this.firstName = firstName;
   this.lastName = lastName;
this.getFullName = () => <code>${this.firstName} ${this.lastName}</code>;
    }
const agent = new Person('前端', '小智');
agent.getFullName();        // => '前端 小智'
  execute(agent.getFullName); // => '前端 小智'
  ```

箭头函数以词法方式绑定this。 简单来说,它使用来自其定义的外部函数this的值。

建议在需要使用外部函数上下文的所有情况下都使用箭头函数。

4. 绑定上下文

现在让咱们更进一步,使用ES6中的类重构Person

代码语言:javascript
复制
```JavaScript
 class Person {
     constructor(firstName, lastName) {
       this.firstName = firstName;
     this.lastName = lastName;
   }
getFullName() {
       return <code>${this.firstName} ${this.lastName}</code>;
   }
 }
const agent = new Person('前端', '小智');
agent.getFullName();        // => '前端 小智'
  execute(agent.getFullName); // => 'undefined undefined'
  ```

不幸的是,即使使用新的类语法,execute(agent.getFullName)仍然返回“undefined undefined”

在类的情况下,使用附加的变量self或箭头函数来修复this的指向是行不通的。

但是有一个涉及bind()方法的技巧,它将方法的上下文绑定到构造函数中:

代码语言:javascript
复制
```JavaScript
 class Person {
     constructor(firstName, lastName) {
       this.firstName = firstName;
     this.lastName = lastName;
<pre><code>this.getFullName = this.getFullName.bind(this);
 </code></pre>
}
getFullName() {
       return <code>${this.firstName} ${this.lastName}</code>;
   }
 }
const agent = new Person('前端', '小智');
agent.getFullName();        // => '前端 小智'
  execute(agent.getFullName); // => '前端 小智'
  ```

构造函数中的this.getFullName = this.getFullName.bind(this)将方法getFullName()绑定到类实例。

execute(agent.getFullName) 按预期工作,返回'前端 小智'

5. 胖箭头方法

bind 方式有点太过冗长,咱们可以使用胖箭头的方式:

代码语言:javascript
复制
```JavaScript
 class Person {
     constructor(firstName, lastName) {
       this.firstName = firstName;
     this.lastName = lastName;
   }
getFullName = () => {
          return <code>${this.firstName} ${this.lastName}</code>;
   }
 }
const agent = new Person('前端', '小智');
agent.getFullName();        // => '前端 小智'
  execute(agent.getFullName); // => '前端 小智'
  ```

胖箭头方法getFullName =() =>{…}绑定到类实例,即使将方法与其对象分离。

这种方法是在类中绑定this的最有效和最简洁的方法。

6. 总结

与对象分离的方法会产生 this 指向不正确问题。静态地绑定this,可以手动使用一个附加变量self来保存正确的上下文对象。然而,更好的替代方法是使用箭头函数,其本质上是为了在词法上绑定this

在类中,可以使用bind()方法手动绑定构造函数中的类方法。当然如果你不用使用 bind 这种冗长方式,也可以使用简洁方便的胖箭头表示方法。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 2. 关闭上下文
  • 3.使用箭头函数
  • 4. 绑定上下文
  • 5. 胖箭头方法
  • 6. 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档